spessasynth_core 4.4.6 → 4.4.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 +367 -48
- package/dist/index.js +612 -194
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -411,7 +411,7 @@ var RIFFChunk = class RIFFChunk {
|
|
|
411
411
|
}
|
|
412
412
|
const parts = [getStringBytes(headerWritten), sizeBytes];
|
|
413
413
|
if (isList) parts.push(getStringBytes(header));
|
|
414
|
-
parts.push(
|
|
414
|
+
for (const chunk of chunks) parts.push(chunk);
|
|
415
415
|
if (totalSize % 2 !== 0) parts.push(/* @__PURE__ */ new Uint8Array(1));
|
|
416
416
|
return parts;
|
|
417
417
|
}
|
|
@@ -511,8 +511,8 @@ function audioToWav(audioData, sampleRate, options = DEFAULT_WAV_WRITE_OPTIONS)
|
|
|
511
511
|
let cueChunk = new IndexedByteArray(0);
|
|
512
512
|
const cueOn = loop?.end !== void 0 && loop?.start !== void 0;
|
|
513
513
|
if (cueOn) {
|
|
514
|
-
const loopStartSamples = Math.
|
|
515
|
-
const loopEndSamples = Math.
|
|
514
|
+
const loopStartSamples = Math.round(loop.start * sampleRate);
|
|
515
|
+
const loopEndSamples = Math.round(loop.end * sampleRate);
|
|
516
516
|
const cueStart = new IndexedByteArray(24);
|
|
517
517
|
writeLittleEndianIndexed(cueStart, 0, 4);
|
|
518
518
|
writeLittleEndianIndexed(cueStart, 0, 4);
|
|
@@ -629,6 +629,105 @@ function audioToWav(audioData, sampleRate, options = DEFAULT_WAV_WRITE_OPTIONS)
|
|
|
629
629
|
if (cueOn) wavData.set(cueChunk, offset);
|
|
630
630
|
return wavData.buffer;
|
|
631
631
|
}
|
|
632
|
+
// src/utils/read_wav.ts
|
|
633
|
+
/**
|
|
634
|
+
* Reads a WAVE file into PCM audio data, the counterpart of {@link audioToWav}.
|
|
635
|
+
*
|
|
636
|
+
* It reads 8, 16, 24 and 32-bit integer PCM and 32 and 64-bit float files (including `WAVE_FORMAT_EXTENSIBLE`).
|
|
637
|
+
* The loop is read from the first loop of the `smpl` chunk. Without one, the first two points of the `cue ` chunk
|
|
638
|
+
* are taken as the loop start and end, which is how {@link audioToWav} writes it.
|
|
639
|
+
* Other files may use cue points as plain markers, so such a loop is only reliable for files written by core.
|
|
640
|
+
*
|
|
641
|
+
* > **Note**
|
|
642
|
+
* >
|
|
643
|
+
* > The returned loop end is exclusive, like the SoundFont loop end.
|
|
644
|
+
*
|
|
645
|
+
* @param buffer The binary WAVE file.
|
|
646
|
+
* @returns The audio data (one array per channel), the sample rate and the loop, if the file has one.
|
|
647
|
+
* @throws Error if the file is not a valid or supported WAVE file.
|
|
648
|
+
*
|
|
649
|
+
* @group Utilities
|
|
650
|
+
*/
|
|
651
|
+
function wavToAudio(buffer) {
|
|
652
|
+
const view = new DataView(buffer);
|
|
653
|
+
const bytes = new Uint8Array(buffer);
|
|
654
|
+
const fourCC = (at) => String.fromCodePoint(bytes[at], bytes[at + 1], bytes[at + 2], bytes[at + 3]);
|
|
655
|
+
if (buffer.byteLength < 12 || fourCC(0) !== "RIFF" || fourCC(8) !== "WAVE") throw new Error("Not a WAVE file.");
|
|
656
|
+
let format = 0;
|
|
657
|
+
let channels = 0;
|
|
658
|
+
let sampleRate = 0;
|
|
659
|
+
let bitsPerSample = 0;
|
|
660
|
+
let dataStart = -1;
|
|
661
|
+
let dataSize = 0;
|
|
662
|
+
let smplLoop;
|
|
663
|
+
const cuePoints = new Array();
|
|
664
|
+
let pos = 12;
|
|
665
|
+
while (pos + 8 <= buffer.byteLength) {
|
|
666
|
+
const id = fourCC(pos);
|
|
667
|
+
const size = view.getUint32(pos + 4, true);
|
|
668
|
+
const body = pos + 8;
|
|
669
|
+
const available = Math.min(size, buffer.byteLength - body);
|
|
670
|
+
switch (id) {
|
|
671
|
+
case "fmt ":
|
|
672
|
+
format = view.getUint16(body, true);
|
|
673
|
+
channels = view.getUint16(body + 2, true);
|
|
674
|
+
sampleRate = view.getUint32(body + 4, true);
|
|
675
|
+
bitsPerSample = view.getUint16(body + 14, true);
|
|
676
|
+
if (format === 65534 && size >= 26) format = view.getUint16(body + 24, true);
|
|
677
|
+
break;
|
|
678
|
+
case "data":
|
|
679
|
+
dataStart = body;
|
|
680
|
+
dataSize = available;
|
|
681
|
+
break;
|
|
682
|
+
case "smpl":
|
|
683
|
+
if ((size >= 36 ? view.getUint32(body + 28, true) : 0) > 0 && size >= 60) smplLoop = {
|
|
684
|
+
start: view.getUint32(body + 36 + 8, true),
|
|
685
|
+
end: view.getUint32(body + 36 + 12, true) + 1
|
|
686
|
+
};
|
|
687
|
+
break;
|
|
688
|
+
case "cue ": {
|
|
689
|
+
const count = view.getUint32(body, true);
|
|
690
|
+
for (let i = 0; i < count && 4 + (i + 1) * 24 <= size; i++) cuePoints.push(view.getUint32(body + 4 + i * 24 + 20, true));
|
|
691
|
+
break;
|
|
692
|
+
}
|
|
693
|
+
}
|
|
694
|
+
pos = body + size + size % 2;
|
|
695
|
+
}
|
|
696
|
+
if (channels === 0 || dataStart === -1) throw new Error("The WAVE file has no fmt or data chunk!");
|
|
697
|
+
const bytesPerSample = bitsPerSample >> 3;
|
|
698
|
+
const isFloat = format === 3;
|
|
699
|
+
if (format !== 1 && format !== 3 || (isFloat ? ![4, 8].includes(bytesPerSample) : bytesPerSample < 1 || bytesPerSample > 4)) throw new Error(`Unsupported WAVE format ${format} with ${bitsPerSample} bits per sample.`);
|
|
700
|
+
const frames = Math.floor(dataSize / (bytesPerSample * channels));
|
|
701
|
+
const audioData = Array.from({ length: channels }, () => new Float32Array(frames));
|
|
702
|
+
for (let i = 0; i < frames; i++) for (let c = 0; c < channels; c++) {
|
|
703
|
+
const at = dataStart + (i * channels + c) * bytesPerSample;
|
|
704
|
+
let value;
|
|
705
|
+
if (isFloat) value = bytesPerSample === 4 ? view.getFloat32(at, true) : view.getFloat64(at, true);
|
|
706
|
+
else switch (bytesPerSample) {
|
|
707
|
+
case 1:
|
|
708
|
+
value = (view.getUint8(at) - 128) / 128;
|
|
709
|
+
break;
|
|
710
|
+
case 2:
|
|
711
|
+
value = view.getInt16(at, true) / 32768;
|
|
712
|
+
break;
|
|
713
|
+
case 3:
|
|
714
|
+
value = ((bytes[at + 2] << 24 | bytes[at + 1] << 16 | bytes[at] << 8) >> 8) / 8388608;
|
|
715
|
+
break;
|
|
716
|
+
default: value = view.getInt32(at, true) / 2147483648;
|
|
717
|
+
}
|
|
718
|
+
audioData[c][i] = value;
|
|
719
|
+
}
|
|
720
|
+
let loop = smplLoop;
|
|
721
|
+
if (!loop && cuePoints.length >= 2) loop = {
|
|
722
|
+
start: cuePoints[0],
|
|
723
|
+
end: cuePoints[1]
|
|
724
|
+
};
|
|
725
|
+
return {
|
|
726
|
+
audioData,
|
|
727
|
+
sampleRate,
|
|
728
|
+
loop
|
|
729
|
+
};
|
|
730
|
+
}
|
|
632
731
|
// src/utils/loggin.ts
|
|
633
732
|
/**
|
|
634
733
|
* SpessaSynth can print out additional info to the console or print nothing at all.
|
|
@@ -1603,56 +1702,43 @@ const GeneratorLimits = Object.freeze({
|
|
|
1603
1702
|
min: -12e3,
|
|
1604
1703
|
max: 12e3,
|
|
1605
1704
|
def: 0,
|
|
1606
|
-
nrpn: 2
|
|
1607
|
-
presetMin: -24e3,
|
|
1608
|
-
presetMax: 24e3
|
|
1705
|
+
nrpn: 2
|
|
1609
1706
|
},
|
|
1610
1707
|
[GeneratorTypes.vibLfoToPitch]: {
|
|
1611
1708
|
min: -12e3,
|
|
1612
1709
|
max: 12e3,
|
|
1613
1710
|
def: 0,
|
|
1614
|
-
nrpn: 2
|
|
1615
|
-
presetMin: -24e3,
|
|
1616
|
-
presetMax: 24e3
|
|
1711
|
+
nrpn: 2
|
|
1617
1712
|
},
|
|
1618
1713
|
[GeneratorTypes.modEnvToPitch]: {
|
|
1619
1714
|
min: -12e3,
|
|
1620
1715
|
max: 12e3,
|
|
1621
1716
|
def: 0,
|
|
1622
|
-
nrpn: 2
|
|
1623
|
-
presetMin: -24e3,
|
|
1624
|
-
presetMax: 24e3
|
|
1717
|
+
nrpn: 2
|
|
1625
1718
|
},
|
|
1626
1719
|
[GeneratorTypes.initialFilterFc]: {
|
|
1627
1720
|
min: 1500,
|
|
1628
1721
|
max: 13500,
|
|
1629
1722
|
def: 13500,
|
|
1630
|
-
nrpn: 2
|
|
1631
|
-
presetMin: -21e3,
|
|
1632
|
-
presetMax: 21e3
|
|
1723
|
+
nrpn: 2
|
|
1633
1724
|
},
|
|
1634
1725
|
[GeneratorTypes.initialFilterQ]: {
|
|
1635
1726
|
min: 0,
|
|
1636
1727
|
max: 960,
|
|
1637
1728
|
def: 0,
|
|
1638
|
-
nrpn: 1
|
|
1639
|
-
presetMin: -960
|
|
1729
|
+
nrpn: 1
|
|
1640
1730
|
},
|
|
1641
1731
|
[GeneratorTypes.modLfoToFilterFc]: {
|
|
1642
1732
|
min: -12e3,
|
|
1643
1733
|
max: 12e3,
|
|
1644
1734
|
def: 0,
|
|
1645
|
-
nrpn: 2
|
|
1646
|
-
presetMin: -24e3,
|
|
1647
|
-
presetMax: 24e3
|
|
1735
|
+
nrpn: 2
|
|
1648
1736
|
},
|
|
1649
1737
|
[GeneratorTypes.modEnvToFilterFc]: {
|
|
1650
1738
|
min: -12e3,
|
|
1651
1739
|
max: 12e3,
|
|
1652
1740
|
def: 0,
|
|
1653
|
-
nrpn: 2
|
|
1654
|
-
presetMin: -24e3,
|
|
1655
|
-
presetMax: 24e3
|
|
1741
|
+
nrpn: 2
|
|
1656
1742
|
},
|
|
1657
1743
|
[GeneratorTypes.endAddrsCoarseOffset]: {
|
|
1658
1744
|
min: -32768,
|
|
@@ -1664,189 +1750,145 @@ const GeneratorLimits = Object.freeze({
|
|
|
1664
1750
|
min: -960,
|
|
1665
1751
|
max: 960,
|
|
1666
1752
|
def: 0,
|
|
1667
|
-
nrpn: 1
|
|
1668
|
-
presetMin: -1920,
|
|
1669
|
-
presetMax: 1920
|
|
1753
|
+
nrpn: 1
|
|
1670
1754
|
},
|
|
1671
1755
|
[GeneratorTypes.chorusEffectsSend]: {
|
|
1672
1756
|
min: 0,
|
|
1673
1757
|
max: 1e3,
|
|
1674
1758
|
def: 0,
|
|
1675
|
-
nrpn: 1
|
|
1676
|
-
presetMin: -1e3
|
|
1759
|
+
nrpn: 1
|
|
1677
1760
|
},
|
|
1678
1761
|
[GeneratorTypes.reverbEffectsSend]: {
|
|
1679
1762
|
min: 0,
|
|
1680
1763
|
max: 1e3,
|
|
1681
1764
|
def: 0,
|
|
1682
|
-
nrpn: 1
|
|
1683
|
-
presetMin: -1e3
|
|
1765
|
+
nrpn: 1
|
|
1684
1766
|
},
|
|
1685
1767
|
[GeneratorTypes.pan]: {
|
|
1686
1768
|
min: -500,
|
|
1687
1769
|
max: 500,
|
|
1688
1770
|
def: 0,
|
|
1689
|
-
nrpn: 1
|
|
1690
|
-
presetMin: -1e3,
|
|
1691
|
-
presetMax: 1e3
|
|
1771
|
+
nrpn: 1
|
|
1692
1772
|
},
|
|
1693
1773
|
[GeneratorTypes.delayModLFO]: {
|
|
1694
1774
|
min: -12e3,
|
|
1695
1775
|
max: 5e3,
|
|
1696
1776
|
def: -12e3,
|
|
1697
|
-
nrpn: 2
|
|
1698
|
-
presetMin: -21e3,
|
|
1699
|
-
presetMax: 21e3
|
|
1777
|
+
nrpn: 2
|
|
1700
1778
|
},
|
|
1701
1779
|
[GeneratorTypes.freqModLFO]: {
|
|
1702
1780
|
min: -16e3,
|
|
1703
1781
|
max: 4500,
|
|
1704
1782
|
def: 0,
|
|
1705
|
-
nrpn: 4
|
|
1706
|
-
presetMin: -21e3,
|
|
1707
|
-
presetMax: 21e3
|
|
1783
|
+
nrpn: 4
|
|
1708
1784
|
},
|
|
1709
1785
|
[GeneratorTypes.delayVibLFO]: {
|
|
1710
1786
|
min: -12e3,
|
|
1711
1787
|
max: 5e3,
|
|
1712
1788
|
def: -12e3,
|
|
1713
|
-
nrpn: 2
|
|
1714
|
-
presetMin: -21e3,
|
|
1715
|
-
presetMax: 21e3
|
|
1789
|
+
nrpn: 2
|
|
1716
1790
|
},
|
|
1717
1791
|
[GeneratorTypes.freqVibLFO]: {
|
|
1718
1792
|
min: -16e3,
|
|
1719
1793
|
max: 4500,
|
|
1720
1794
|
def: 0,
|
|
1721
|
-
nrpn: 4
|
|
1722
|
-
presetMin: -21e3,
|
|
1723
|
-
presetMax: 21e3
|
|
1795
|
+
nrpn: 4
|
|
1724
1796
|
},
|
|
1725
1797
|
[GeneratorTypes.delayModEnv]: {
|
|
1726
1798
|
min: -32768,
|
|
1727
1799
|
max: 5e3,
|
|
1728
1800
|
def: -32768,
|
|
1729
|
-
nrpn: 2
|
|
1730
|
-
presetMin: -21e3,
|
|
1731
|
-
presetMax: 21e3
|
|
1801
|
+
nrpn: 2
|
|
1732
1802
|
},
|
|
1733
1803
|
[GeneratorTypes.attackModEnv]: {
|
|
1734
1804
|
min: -32768,
|
|
1735
1805
|
max: 8e3,
|
|
1736
1806
|
def: -32768,
|
|
1737
|
-
nrpn: 2
|
|
1738
|
-
presetMin: -21e3,
|
|
1739
|
-
presetMax: 21e3
|
|
1807
|
+
nrpn: 2
|
|
1740
1808
|
},
|
|
1741
1809
|
[GeneratorTypes.holdModEnv]: {
|
|
1742
1810
|
min: -12e3,
|
|
1743
1811
|
max: 5e3,
|
|
1744
1812
|
def: -12e3,
|
|
1745
|
-
nrpn: 2
|
|
1746
|
-
presetMin: -21e3,
|
|
1747
|
-
presetMax: 21e3
|
|
1813
|
+
nrpn: 2
|
|
1748
1814
|
},
|
|
1749
1815
|
[GeneratorTypes.decayModEnv]: {
|
|
1750
1816
|
min: -12e3,
|
|
1751
1817
|
max: 8e3,
|
|
1752
1818
|
def: -12e3,
|
|
1753
|
-
nrpn: 2
|
|
1754
|
-
presetMin: -21e3,
|
|
1755
|
-
presetMax: 21e3
|
|
1819
|
+
nrpn: 2
|
|
1756
1820
|
},
|
|
1757
1821
|
[GeneratorTypes.sustainModEnv]: {
|
|
1758
1822
|
min: 0,
|
|
1759
1823
|
max: 1e3,
|
|
1760
1824
|
def: 0,
|
|
1761
|
-
nrpn: 1
|
|
1762
|
-
presetMin: -1e3
|
|
1825
|
+
nrpn: 1
|
|
1763
1826
|
},
|
|
1764
1827
|
[GeneratorTypes.releaseModEnv]: {
|
|
1765
1828
|
min: -12e3,
|
|
1766
1829
|
max: 8e3,
|
|
1767
1830
|
def: -12e3,
|
|
1768
|
-
nrpn: 2
|
|
1769
|
-
presetMin: -21e3,
|
|
1770
|
-
presetMax: 21e3
|
|
1831
|
+
nrpn: 2
|
|
1771
1832
|
},
|
|
1772
1833
|
[GeneratorTypes.keyNumToModEnvHold]: {
|
|
1773
1834
|
min: -1200,
|
|
1774
1835
|
max: 1200,
|
|
1775
1836
|
def: 0,
|
|
1776
|
-
nrpn: 1
|
|
1777
|
-
presetMin: -2400,
|
|
1778
|
-
presetMax: 2400
|
|
1837
|
+
nrpn: 1
|
|
1779
1838
|
},
|
|
1780
1839
|
[GeneratorTypes.keyNumToModEnvDecay]: {
|
|
1781
1840
|
min: -1200,
|
|
1782
1841
|
max: 1200,
|
|
1783
1842
|
def: 0,
|
|
1784
|
-
nrpn: 1
|
|
1785
|
-
presetMin: -2400,
|
|
1786
|
-
presetMax: 2400
|
|
1843
|
+
nrpn: 1
|
|
1787
1844
|
},
|
|
1788
1845
|
[GeneratorTypes.delayVolEnv]: {
|
|
1789
1846
|
min: -12e3,
|
|
1790
1847
|
max: 5e3,
|
|
1791
1848
|
def: -12e3,
|
|
1792
|
-
nrpn: 2
|
|
1793
|
-
presetMin: -21e3,
|
|
1794
|
-
presetMax: 21e3
|
|
1849
|
+
nrpn: 2
|
|
1795
1850
|
},
|
|
1796
1851
|
[GeneratorTypes.attackVolEnv]: {
|
|
1797
1852
|
min: -12e3,
|
|
1798
1853
|
max: 8e3,
|
|
1799
1854
|
def: -12e3,
|
|
1800
|
-
nrpn: 2
|
|
1801
|
-
presetMin: -21e3,
|
|
1802
|
-
presetMax: 21e3
|
|
1855
|
+
nrpn: 2
|
|
1803
1856
|
},
|
|
1804
1857
|
[GeneratorTypes.holdVolEnv]: {
|
|
1805
1858
|
min: -12e3,
|
|
1806
1859
|
max: 5e3,
|
|
1807
1860
|
def: -12e3,
|
|
1808
|
-
nrpn: 2
|
|
1809
|
-
presetMin: -21e3,
|
|
1810
|
-
presetMax: 21e3
|
|
1861
|
+
nrpn: 2
|
|
1811
1862
|
},
|
|
1812
1863
|
[GeneratorTypes.decayVolEnv]: {
|
|
1813
1864
|
min: -12e3,
|
|
1814
1865
|
max: 8e3,
|
|
1815
1866
|
def: -12e3,
|
|
1816
|
-
nrpn: 2
|
|
1817
|
-
presetMin: -21e3,
|
|
1818
|
-
presetMax: 21e3
|
|
1867
|
+
nrpn: 2
|
|
1819
1868
|
},
|
|
1820
1869
|
[GeneratorTypes.sustainVolEnv]: {
|
|
1821
1870
|
min: 0,
|
|
1822
1871
|
max: 1440,
|
|
1823
1872
|
def: 0,
|
|
1824
|
-
nrpn: 1
|
|
1825
|
-
presetMin: -1440
|
|
1873
|
+
nrpn: 1
|
|
1826
1874
|
},
|
|
1827
1875
|
[GeneratorTypes.releaseVolEnv]: {
|
|
1828
1876
|
min: -12e3,
|
|
1829
1877
|
max: 8e3,
|
|
1830
1878
|
def: -12e3,
|
|
1831
|
-
nrpn: 2
|
|
1832
|
-
presetMin: -21e3,
|
|
1833
|
-
presetMax: 21e3
|
|
1879
|
+
nrpn: 2
|
|
1834
1880
|
},
|
|
1835
1881
|
[GeneratorTypes.keyNumToVolEnvHold]: {
|
|
1836
1882
|
min: -1200,
|
|
1837
1883
|
max: 1200,
|
|
1838
1884
|
def: 0,
|
|
1839
|
-
nrpn: 1
|
|
1840
|
-
presetMin: -2400,
|
|
1841
|
-
presetMax: 2400
|
|
1885
|
+
nrpn: 1
|
|
1842
1886
|
},
|
|
1843
1887
|
[GeneratorTypes.keyNumToVolEnvDecay]: {
|
|
1844
1888
|
min: -1200,
|
|
1845
1889
|
max: 1200,
|
|
1846
1890
|
def: 0,
|
|
1847
|
-
nrpn: 1
|
|
1848
|
-
presetMin: -2400,
|
|
1849
|
-
presetMax: 2400
|
|
1891
|
+
nrpn: 1
|
|
1850
1892
|
},
|
|
1851
1893
|
[GeneratorTypes.startloopAddrsCoarseOffset]: {
|
|
1852
1894
|
min: -32768,
|
|
@@ -1870,8 +1912,7 @@ const GeneratorLimits = Object.freeze({
|
|
|
1870
1912
|
min: 0,
|
|
1871
1913
|
max: 1440,
|
|
1872
1914
|
def: 0,
|
|
1873
|
-
nrpn: 1
|
|
1874
|
-
presetMin: -1440
|
|
1915
|
+
nrpn: 1
|
|
1875
1916
|
},
|
|
1876
1917
|
[GeneratorTypes.endloopAddrsCoarseOffset]: {
|
|
1877
1918
|
min: -32768,
|
|
@@ -1883,28 +1924,23 @@ const GeneratorLimits = Object.freeze({
|
|
|
1883
1924
|
min: -120,
|
|
1884
1925
|
max: 120,
|
|
1885
1926
|
def: 0,
|
|
1886
|
-
nrpn: 1
|
|
1887
|
-
presetMin: -240,
|
|
1888
|
-
presetMax: 240
|
|
1927
|
+
nrpn: 1
|
|
1889
1928
|
},
|
|
1890
1929
|
[GeneratorTypes.fineTune]: {
|
|
1891
1930
|
min: -12700,
|
|
1892
1931
|
max: 12700,
|
|
1893
1932
|
def: 0,
|
|
1894
|
-
nrpn: 1
|
|
1895
|
-
presetMin: -198,
|
|
1896
|
-
presetMax: 198
|
|
1933
|
+
nrpn: 1
|
|
1897
1934
|
},
|
|
1898
1935
|
[GeneratorTypes.scaleTuning]: {
|
|
1899
1936
|
min: 0,
|
|
1900
1937
|
max: 1200,
|
|
1901
1938
|
def: 100,
|
|
1902
|
-
nrpn: 1
|
|
1903
|
-
presetMin: -1200
|
|
1939
|
+
nrpn: 1
|
|
1904
1940
|
},
|
|
1905
1941
|
[GeneratorTypes.exclusiveClass]: {
|
|
1906
1942
|
min: 0,
|
|
1907
|
-
max:
|
|
1943
|
+
max: 32767,
|
|
1908
1944
|
def: 0,
|
|
1909
1945
|
nrpn: 0
|
|
1910
1946
|
},
|
|
@@ -2246,6 +2282,7 @@ function resetChannelInternal(sendCCEvents = true) {
|
|
|
2246
2282
|
this.resetMIDIParameter("modulationDepth");
|
|
2247
2283
|
this.resetMIDIParameter("rxChannel");
|
|
2248
2284
|
this.resetMIDIParameter("efxAssign");
|
|
2285
|
+
this.resetMIDIParameter("eqEnabled");
|
|
2249
2286
|
this.resetMIDIParameter("polyMode");
|
|
2250
2287
|
this.resetMIDIParameter("keyShift");
|
|
2251
2288
|
this.resetMIDIParameter("fineTune");
|
|
@@ -2467,6 +2504,7 @@ function applySnapshotInternal(midi, snapshot) {
|
|
|
2467
2504
|
gsReverbParams: snapshot.systemParameters.gsReverbLock ? snapshot.gsReverbProcessor : void 0,
|
|
2468
2505
|
gsChorusParams: snapshot.systemParameters.gsChorusLock ? snapshot.gsChorusProcessor : void 0,
|
|
2469
2506
|
gsDelayParams: snapshot.systemParameters.gsDelayLock ? snapshot.gsDelayProcessor : void 0,
|
|
2507
|
+
gsEqualizerParams: snapshot.systemParameters.gsEqualizerLock ? snapshot.gsEqualizerProcessor : void 0,
|
|
2470
2508
|
gsInsertionParams: snapshot.systemParameters.gsInsertionLock ? snapshot.gsInsertionBlock : void 0,
|
|
2471
2509
|
xgReverbParams: snapshot.systemParameters.xgReverbLock ? snapshot.xgReverbBlock : void 0,
|
|
2472
2510
|
xgChorusParams: snapshot.systemParameters.xgChorusLock ? snapshot.xgChorusBlock : void 0,
|
|
@@ -2779,7 +2817,8 @@ const GSInsertionAddressMap = {
|
|
|
2779
2817
|
controlSource1: 27,
|
|
2780
2818
|
controlDepth1: 28,
|
|
2781
2819
|
controlSource2: 29,
|
|
2782
|
-
controlDepth2: 30
|
|
2820
|
+
controlDepth2: 30,
|
|
2821
|
+
sendEQSwitch: 31
|
|
2783
2822
|
};
|
|
2784
2823
|
const GSChorusAddressMap = {
|
|
2785
2824
|
macro: 56,
|
|
@@ -2805,6 +2844,12 @@ const GSDelayAddressMap = {
|
|
|
2805
2844
|
feedback: 89,
|
|
2806
2845
|
sendLevelToReverb: 90
|
|
2807
2846
|
};
|
|
2847
|
+
const GSEqualizerAddressMap = {
|
|
2848
|
+
lowFrequency: 0,
|
|
2849
|
+
lowGain: 1,
|
|
2850
|
+
highFrequency: 2,
|
|
2851
|
+
highGain: 3
|
|
2852
|
+
};
|
|
2808
2853
|
/**
|
|
2809
2854
|
* The SysEx addresses of the channel MIDI parameters that GS and XG both set per part.
|
|
2810
2855
|
* `gs` is the third address byte of GS PATCH PART (`40 1x xx`) and `xg` is the third address byte of
|
|
@@ -2875,6 +2920,10 @@ const NRPNDrumParamMap = {
|
|
|
2875
2920
|
const NRPNDrumParamByMSB = new Map(Object.entries(NRPNDrumParamMap).map(([parameter, msb]) => [msb, parameter]));
|
|
2876
2921
|
// src/midi/midi_tools/midi_utils.ts
|
|
2877
2922
|
/**
|
|
2923
|
+
* The GS equalizer parameters by their address (`40 02 xx`).
|
|
2924
|
+
*/
|
|
2925
|
+
const GS_EQUALIZER_PARAMETER_BY_ADDRESS = new Map(Object.entries(GSEqualizerAddressMap).map(([name, address]) => [address, name]));
|
|
2926
|
+
/**
|
|
2878
2927
|
* The named GS insertion parameters by their address (`40 03 xx`).
|
|
2879
2928
|
*/
|
|
2880
2929
|
const GS_INSERTION_PARAMETER_BY_ADDRESS = new Map(Object.entries(GSInsertionAddressMap).map(([name, address]) => [address, name]));
|
|
@@ -3017,20 +3066,33 @@ var MIDIUtils = class MIDIUtils {
|
|
|
3017
3066
|
return this.gsMessage(ticks, 64, 1, a3, [value]);
|
|
3018
3067
|
}
|
|
3019
3068
|
/**
|
|
3069
|
+
* Returns a MIDI event needed to set the given GS Equalizer Parameter.
|
|
3070
|
+
* @param ticks The MIDI tick time for the output event.
|
|
3071
|
+
* @param parameter The parameter to set.
|
|
3072
|
+
* @param value The value to set it to.
|
|
3073
|
+
* @returns The {@link MIDIMessage} needed to set this GS Equalizer Parameter.
|
|
3074
|
+
*/
|
|
3075
|
+
static setGSEqualizerParameter(ticks, parameter, value) {
|
|
3076
|
+
const a3 = GSEqualizerAddressMap[parameter];
|
|
3077
|
+
if (a3 === void 0) throw new Error(`Invalid equalizer parameter: ${parameter}`);
|
|
3078
|
+
return this.gsMessage(ticks, 64, 2, a3, [value]);
|
|
3079
|
+
}
|
|
3080
|
+
/**
|
|
3020
3081
|
* Returns a MIDI event needed to set the given GS Insertion Parameter.
|
|
3021
3082
|
* @param ticks The MIDI tick time for the output event.
|
|
3022
3083
|
* @param parameter The parameter to set: `"type"`, a send level or EFX control name (see {@link GSInsertionParameter}),
|
|
3023
3084
|
* or a 0-based effect-specific parameter number (0-19).
|
|
3024
|
-
* @param value The value to set it to.
|
|
3085
|
+
* @param value The value to set it to. A boolean for `"sendEQSwitch"`.
|
|
3025
3086
|
* @returns The {@link MIDIMessage} needed to set this GS Insertion Parameter.
|
|
3026
3087
|
*/
|
|
3027
3088
|
static setGSInsertionParameter(ticks, parameter, value) {
|
|
3089
|
+
const data = typeof value === "boolean" ? value ? 1 : 0 : value;
|
|
3028
3090
|
switch (parameter) {
|
|
3029
|
-
case "type": return this.gsMessage(ticks, 64, 3, 0, [
|
|
3091
|
+
case "type": return this.gsMessage(ticks, 64, 3, 0, [data >> 8 & 127, data & 127]);
|
|
3030
3092
|
default:
|
|
3031
|
-
if (typeof parameter === "string") return this.gsMessage(ticks, 64, 3, GSInsertionAddressMap[parameter], [
|
|
3093
|
+
if (typeof parameter === "string") return this.gsMessage(ticks, 64, 3, GSInsertionAddressMap[parameter], [data]);
|
|
3032
3094
|
if (!Number.isInteger(parameter) || parameter < 0 || parameter > 19) throw new Error(`Invalid insertion parameter: ${parameter}`);
|
|
3033
|
-
return this.gsMessage(ticks, 64, 3, parameter + 3, [
|
|
3095
|
+
return this.gsMessage(ticks, 64, 3, parameter + 3, [data]);
|
|
3034
3096
|
}
|
|
3035
3097
|
}
|
|
3036
3098
|
/**
|
|
@@ -3238,6 +3300,7 @@ var MIDIUtils = class MIDIUtils {
|
|
|
3238
3300
|
case "drumMap":
|
|
3239
3301
|
case "velocitySenseDepth":
|
|
3240
3302
|
case "velocitySenseOffset": return [MIDIUtils.partParamMessage(ticks, channel, system, parameter, value)];
|
|
3303
|
+
case "eqEnabled": return [MIDIUtils.gsMessage(ticks, 64, 64 | MIDIUtils.channelToGSPart(channel), 32, [value ? 1 : 0])];
|
|
3241
3304
|
case "efxAssign": return [MIDIUtils.gsMessage(ticks, 64, 64 | MIDIUtils.channelToGSPart(channel), 34, [value ? 1 : 0])];
|
|
3242
3305
|
case "dryLevel": return [MIDIUtils.xgMessage(ticks, 8, channel, 17, [value])];
|
|
3243
3306
|
}
|
|
@@ -3970,6 +4033,12 @@ var MIDIUtils = class MIDIUtils {
|
|
|
3970
4033
|
controller: MIDIControllers.reverbDepth,
|
|
3971
4034
|
value
|
|
3972
4035
|
}];
|
|
4036
|
+
case 20: return [{
|
|
4037
|
+
type: "Controller Change",
|
|
4038
|
+
channel,
|
|
4039
|
+
controller: MIDIControllers.variationDepth,
|
|
4040
|
+
value
|
|
4041
|
+
}];
|
|
3973
4042
|
case 21: return [{
|
|
3974
4043
|
type: "Controller Change",
|
|
3975
4044
|
channel,
|
|
@@ -4438,6 +4507,15 @@ var MIDIUtils = class MIDIUtils {
|
|
|
4438
4507
|
value
|
|
4439
4508
|
}];
|
|
4440
4509
|
}
|
|
4510
|
+
if (a2 === 2) {
|
|
4511
|
+
const parameter = GS_EQUALIZER_PARAMETER_BY_ADDRESS.get(a3);
|
|
4512
|
+
if (parameter === void 0) return [OTHER];
|
|
4513
|
+
return [{
|
|
4514
|
+
type: "GS Equalizer Param",
|
|
4515
|
+
parameter,
|
|
4516
|
+
value
|
|
4517
|
+
}];
|
|
4518
|
+
}
|
|
4441
4519
|
if (a2 === 3) {
|
|
4442
4520
|
switch (a3) {
|
|
4443
4521
|
case 0: return [{
|
|
@@ -4447,6 +4525,11 @@ var MIDIUtils = class MIDIUtils {
|
|
|
4447
4525
|
}];
|
|
4448
4526
|
}
|
|
4449
4527
|
const named = GS_INSERTION_PARAMETER_BY_ADDRESS.get(a3);
|
|
4528
|
+
if (named === "sendEQSwitch") return [{
|
|
4529
|
+
type: "GS Insertion Param",
|
|
4530
|
+
parameter: named,
|
|
4531
|
+
value: value !== 0
|
|
4532
|
+
}];
|
|
4450
4533
|
if (named !== void 0) return [{
|
|
4451
4534
|
type: "GS Insertion Param",
|
|
4452
4535
|
parameter: named,
|
|
@@ -4650,6 +4733,12 @@ var MIDIUtils = class MIDIUtils {
|
|
|
4650
4733
|
controller: MIDIControllers.bankSelectLSB,
|
|
4651
4734
|
value
|
|
4652
4735
|
}];
|
|
4736
|
+
case 32: return [{
|
|
4737
|
+
type: "Channel MIDI Param",
|
|
4738
|
+
channel,
|
|
4739
|
+
parameter: "eqEnabled",
|
|
4740
|
+
value: value === 1
|
|
4741
|
+
}];
|
|
4653
4742
|
case 34: return [{
|
|
4654
4743
|
type: "Channel MIDI Param",
|
|
4655
4744
|
channel,
|
|
@@ -5023,6 +5112,7 @@ var MIDIEditor = class {
|
|
|
5023
5112
|
gsReverbParams;
|
|
5024
5113
|
gsChorusParams;
|
|
5025
5114
|
gsDelayParams;
|
|
5115
|
+
gsEqualizerParams;
|
|
5026
5116
|
gsInsertionParams;
|
|
5027
5117
|
xgReverbParams;
|
|
5028
5118
|
xgChorusParams;
|
|
@@ -5050,10 +5140,11 @@ var MIDIEditor = class {
|
|
|
5050
5140
|
constructor(midi, opts) {
|
|
5051
5141
|
this.midi = midi;
|
|
5052
5142
|
SpessaLog.groupCollapsed("%cApplying changes to the MIDI file...", ConsoleColors.info);
|
|
5053
|
-
const { channels, gsReverbParams, gsChorusParams, gsDelayParams, gsInsertionParams, xgReverbParams, xgChorusParams, xgVariationParams, xgInsertionParams, userDrumParams, midiParams } = opts;
|
|
5143
|
+
const { channels, gsReverbParams, gsChorusParams, gsDelayParams, gsEqualizerParams, gsInsertionParams, xgReverbParams, xgChorusParams, xgVariationParams, xgInsertionParams, userDrumParams, midiParams } = opts;
|
|
5054
5144
|
this.gsReverbParams = gsReverbParams;
|
|
5055
5145
|
this.gsChorusParams = gsChorusParams;
|
|
5056
5146
|
this.gsDelayParams = gsDelayParams;
|
|
5147
|
+
this.gsEqualizerParams = gsEqualizerParams;
|
|
5057
5148
|
this.gsInsertionParams = gsInsertionParams;
|
|
5058
5149
|
this.xgReverbParams = xgReverbParams;
|
|
5059
5150
|
this.xgChorusParams = xgChorusParams;
|
|
@@ -5126,12 +5217,12 @@ var MIDIEditor = class {
|
|
|
5126
5217
|
}
|
|
5127
5218
|
if (this.lastReset !== void 0 && replacedEvents.has(this.lastReset)) this.system = this.fallbackReset ?? "gs";
|
|
5128
5219
|
}
|
|
5129
|
-
if (!this.addedReset && this.midiParams?.system !== "clear" && (this.midiParams?.system !== void 0 || this.fallbackReset !== void 0 || this.gsReverbParams && this.gsReverbParams !== "clear" || this.gsChorusParams && this.gsChorusParams !== "clear" || this.gsDelayParams && this.gsDelayParams !== "clear" || this.gsInsertionParams && this.gsInsertionParams !== "clear" || this.xgReverbParams && this.xgReverbParams !== "clear" || this.xgChorusParams && this.xgChorusParams !== "clear" || this.xgVariationParams && this.xgVariationParams !== "clear" || [...this.xgInsertionParams?.values() ?? []].some((p) => p !== "clear") || this.userDrumSetParams?.size || [...this.channelChanges.values()].some((c) => c.patch && c.patch !== "clear"))) {
|
|
5220
|
+
if (!this.addedReset && this.midiParams?.system !== "clear" && (this.midiParams?.system !== void 0 || this.fallbackReset !== void 0 || this.gsReverbParams && this.gsReverbParams !== "clear" || this.gsChorusParams && this.gsChorusParams !== "clear" || this.gsDelayParams && this.gsDelayParams !== "clear" || this.gsEqualizerParams && this.gsEqualizerParams !== "clear" || this.gsInsertionParams && this.gsInsertionParams !== "clear" || this.xgReverbParams && this.xgReverbParams !== "clear" || this.xgChorusParams && this.xgChorusParams !== "clear" || this.xgVariationParams && this.xgVariationParams !== "clear" || [...this.xgInsertionParams?.values() ?? []].some((p) => p !== "clear") || this.userDrumSetParams?.size || [...this.channelChanges.values()].some((c) => c.patch && c.patch !== "clear"))) {
|
|
5130
5221
|
let index = 0;
|
|
5131
5222
|
const firstTrack = this.midi.tracks[0];
|
|
5132
5223
|
while (index < firstTrack.events.length && firstTrack.events[index].statusByte < MIDIMessageTypes.noteOff) index++;
|
|
5133
5224
|
const hasXG = this.xgReverbParams !== void 0 && this.xgReverbParams !== "clear" || this.xgChorusParams !== void 0 && this.xgChorusParams !== "clear" || this.xgVariationParams !== void 0 && this.xgVariationParams !== "clear" || [...this.xgInsertionParams?.values() ?? []].some((p) => p !== "clear");
|
|
5134
|
-
const hasGS = this.gsReverbParams !== void 0 && this.gsReverbParams !== "clear" || this.gsChorusParams !== void 0 && this.gsChorusParams !== "clear" || this.gsDelayParams !== void 0 && this.gsDelayParams !== "clear" || this.gsInsertionParams !== void 0 && this.gsInsertionParams !== "clear";
|
|
5225
|
+
const hasGS = this.gsReverbParams !== void 0 && this.gsReverbParams !== "clear" || this.gsChorusParams !== void 0 && this.gsChorusParams !== "clear" || this.gsDelayParams !== void 0 && this.gsDelayParams !== "clear" || this.gsEqualizerParams !== void 0 && this.gsEqualizerParams !== "clear" || this.gsInsertionParams !== void 0 && this.gsInsertionParams !== "clear";
|
|
5135
5226
|
const targetSystem = this.midiParams?.system ?? this.fallbackReset ?? (hasXG && !hasGS ? "xg" : "gs");
|
|
5136
5227
|
const event = MIDIUtils.reset(0, targetSystem);
|
|
5137
5228
|
firstTrack.addEvents(index, event);
|
|
@@ -5371,6 +5462,12 @@ var MIDIEditor = class {
|
|
|
5371
5462
|
return;
|
|
5372
5463
|
}
|
|
5373
5464
|
break;
|
|
5465
|
+
case "GS Equalizer Param":
|
|
5466
|
+
if (this.gsEqualizerParams) {
|
|
5467
|
+
this.deleteCurrentEvent();
|
|
5468
|
+
return;
|
|
5469
|
+
}
|
|
5470
|
+
break;
|
|
5374
5471
|
case "GS Insertion Param":
|
|
5375
5472
|
if (this.gsInsertionParams) {
|
|
5376
5473
|
this.deleteCurrentEvent();
|
|
@@ -5785,6 +5882,10 @@ var MIDIEditor = class {
|
|
|
5785
5882
|
const p = this.gsDelayParams;
|
|
5786
5883
|
output.push(MIDIUtils.setGSDelayParameter(targetTicks, "level", p.level), MIDIUtils.setGSDelayParameter(targetTicks, "preLowpass", p.preLowpass), MIDIUtils.setGSDelayParameter(targetTicks, "timeCenter", p.timeCenter), MIDIUtils.setGSDelayParameter(targetTicks, "timeRatioLeft", p.timeRatioLeft), MIDIUtils.setGSDelayParameter(targetTicks, "timeRatioRight", p.timeRatioRight), MIDIUtils.setGSDelayParameter(targetTicks, "levelCenter", p.levelCenter), MIDIUtils.setGSDelayParameter(targetTicks, "levelLeft", p.levelLeft), MIDIUtils.setGSDelayParameter(targetTicks, "levelRight", p.levelRight), MIDIUtils.setGSDelayParameter(targetTicks, "feedback", p.feedback), MIDIUtils.setGSDelayParameter(targetTicks, "sendLevelToReverb", p.sendLevelToReverb));
|
|
5787
5884
|
}
|
|
5885
|
+
if (this.gsEqualizerParams && this.gsEqualizerParams !== "clear") {
|
|
5886
|
+
const p = this.gsEqualizerParams;
|
|
5887
|
+
for (const name of Object.keys(GSEqualizerAddressMap)) output.push(MIDIUtils.setGSEqualizerParameter(targetTicks, name, p[name]));
|
|
5888
|
+
}
|
|
5788
5889
|
if (this.gsInsertionParams && this.gsInsertionParams !== "clear") {
|
|
5789
5890
|
const p = this.gsInsertionParams;
|
|
5790
5891
|
output.push(MIDIUtils.setGSInsertionParameter(targetTicks, "type", p.type));
|
|
@@ -7278,6 +7379,22 @@ var BasicSample = class {
|
|
|
7278
7379
|
return this.linkedTo.length;
|
|
7279
7380
|
}
|
|
7280
7381
|
/**
|
|
7382
|
+
* If the decoded float data is currently held in memory.
|
|
7383
|
+
* Check it before {@link getAudioData} to know if the call has to decode,
|
|
7384
|
+
* then use {@link releaseDecoded} to drop the copy again.
|
|
7385
|
+
*/
|
|
7386
|
+
get isDecoded() {
|
|
7387
|
+
return this.audioData !== void 0;
|
|
7388
|
+
}
|
|
7389
|
+
/**
|
|
7390
|
+
* If the decoded float data can be made again from the stored data.
|
|
7391
|
+
* False for replaced data (set with {@link BasicSample.setAudioData}), which has nothing to decode from.
|
|
7392
|
+
* @internal
|
|
7393
|
+
*/
|
|
7394
|
+
get canRedecode() {
|
|
7395
|
+
return !this.dataOverridden && (this.compressedData !== void 0 || this.s16leData !== void 0);
|
|
7396
|
+
}
|
|
7397
|
+
/**
|
|
7281
7398
|
* Get raw data for writing the file, either a compressed bit stream or signed 16-bit little endian PCM data.
|
|
7282
7399
|
* @param allowVorbis if vorbis file data is allowed.
|
|
7283
7400
|
* @return either s16le or vorbis data.
|
|
@@ -7353,7 +7470,7 @@ var BasicSample = class {
|
|
|
7353
7470
|
* @returns True if the decoded data was dropped.
|
|
7354
7471
|
*/
|
|
7355
7472
|
releaseDecoded() {
|
|
7356
|
-
if (this.audioData &&
|
|
7473
|
+
if (this.audioData && this.canRedecode) {
|
|
7357
7474
|
this.audioData = void 0;
|
|
7358
7475
|
return true;
|
|
7359
7476
|
}
|
|
@@ -7385,12 +7502,13 @@ var BasicSample = class {
|
|
|
7385
7502
|
this.setAudioData(resampled, newSampleRate);
|
|
7386
7503
|
}
|
|
7387
7504
|
/**
|
|
7388
|
-
* Compresses the audio data
|
|
7505
|
+
* Compresses the audio data.
|
|
7389
7506
|
* @param encodeVorbis the compression function to use when compressing.
|
|
7507
|
+
* @returns True if the sample is compressed afterward, false if the encoder failed and it stays uncompressed.
|
|
7390
7508
|
* @internal
|
|
7391
7509
|
*/
|
|
7392
7510
|
async compressSample(encodeVorbis) {
|
|
7393
|
-
if (this.isCompressed) return;
|
|
7511
|
+
if (this.isCompressed) return true;
|
|
7394
7512
|
try {
|
|
7395
7513
|
let audioData = this.getAudioData();
|
|
7396
7514
|
if (this.sampleRate < 8e3 || this.sampleRate > 96e3) {
|
|
@@ -7399,9 +7517,11 @@ var BasicSample = class {
|
|
|
7399
7517
|
}
|
|
7400
7518
|
const compressed = await encodeVorbis(audioData, this.sampleRate);
|
|
7401
7519
|
this.setCompressedData(compressed);
|
|
7520
|
+
return true;
|
|
7402
7521
|
} catch (error) {
|
|
7403
7522
|
SpessaLog.warn(`Failed to compress ${this.name}. Leaving as uncompressed!`, error);
|
|
7404
7523
|
this.compressedData = void 0;
|
|
7524
|
+
return false;
|
|
7405
7525
|
}
|
|
7406
7526
|
}
|
|
7407
7527
|
/**
|
|
@@ -7451,16 +7571,17 @@ var BasicSample = class {
|
|
|
7451
7571
|
}
|
|
7452
7572
|
}
|
|
7453
7573
|
/**
|
|
7454
|
-
* Links the sample to a given instrument
|
|
7455
|
-
* @param instrument the instrument to link to
|
|
7574
|
+
* Links the sample to a given instrument.
|
|
7575
|
+
* @param instrument the instrument to link to.
|
|
7456
7576
|
* @internal
|
|
7457
7577
|
*/
|
|
7458
7578
|
linkTo(instrument) {
|
|
7459
7579
|
this.linkedTo.push(instrument);
|
|
7460
7580
|
}
|
|
7461
7581
|
/**
|
|
7462
|
-
* Unlinks the sample from a given instrument
|
|
7463
|
-
* @param instrument the instrument to unlink from
|
|
7582
|
+
* Unlinks the sample from a given instrument.
|
|
7583
|
+
* @param instrument the instrument to unlink from.
|
|
7584
|
+
* @internal
|
|
7464
7585
|
*/
|
|
7465
7586
|
unlinkFrom(instrument) {
|
|
7466
7587
|
const index = this.linkedTo.indexOf(instrument);
|
|
@@ -7827,12 +7948,12 @@ var ModulatorSource = class ModulatorSource {
|
|
|
7827
7948
|
*/
|
|
7828
7949
|
curveType;
|
|
7829
7950
|
/**
|
|
7830
|
-
*
|
|
7831
|
-
* @param index
|
|
7832
|
-
* @param curveType
|
|
7833
|
-
* @param isCC
|
|
7834
|
-
* @param isBipolar
|
|
7835
|
-
* @param isNegative
|
|
7951
|
+
* Creates a new modulator source.
|
|
7952
|
+
* @param index The controller or MIDI CC number, see `isCC`.
|
|
7953
|
+
* @param curveType How the minimum value approaches the maximum value.
|
|
7954
|
+
* @param isCC If the index is a MIDI CC instead of a general controller.
|
|
7955
|
+
* @param isBipolar If the source is bipolar (-1 to 1) instead of unipolar (0 to 1).
|
|
7956
|
+
* @param isNegative If the direction of the source is flipped.
|
|
7836
7957
|
*/
|
|
7837
7958
|
constructor(index = ModulatorControllerSources.noController, curveType = ModulatorCurveTypes.linear, isCC = false, isBipolar = false, isNegative = false) {
|
|
7838
7959
|
this.isBipolar = isBipolar;
|
|
@@ -8300,14 +8421,13 @@ var BasicLinkedZone = class extends BasicZone {
|
|
|
8300
8421
|
}
|
|
8301
8422
|
/**
|
|
8302
8423
|
* Links the child to the parent, unless this zone was created without linking.
|
|
8303
|
-
* @
|
|
8424
|
+
* Use it to put a zone back after {@link unlinkChild}, for example when undoing a zone deletion.
|
|
8304
8425
|
*/
|
|
8305
8426
|
linkChild() {
|
|
8306
8427
|
if (this.linked) this.child.linkTo(this.parent);
|
|
8307
8428
|
}
|
|
8308
8429
|
/**
|
|
8309
8430
|
* Unlinks the child from the parent, unless this zone was created without linking.
|
|
8310
|
-
* @internal
|
|
8311
8431
|
*/
|
|
8312
8432
|
unlinkChild() {
|
|
8313
8433
|
if (this.linked) this.child.unlinkFrom(this.parent);
|
|
@@ -8695,7 +8815,7 @@ var BasicPreset = class BasicPreset {
|
|
|
8695
8815
|
}
|
|
8696
8816
|
}
|
|
8697
8817
|
finalGenList = finalGenList.filter((g) => g.type !== GeneratorTypes.sampleID && g.type !== GeneratorTypes.keyRange && g.type !== GeneratorTypes.velRange && g.type !== GeneratorTypes.endOper && g.type !== GeneratorTypes.instrument && (!(g.type in GeneratorLimits) || g.value !== GeneratorLimits[g.type].def));
|
|
8698
|
-
const zone = outputInstrument.createZone(instZone.sample);
|
|
8818
|
+
const zone = outputInstrument.createZone(instZone.sample, false);
|
|
8699
8819
|
zone.keyRange = instZoneKeyRange;
|
|
8700
8820
|
zone.velRange = instZoneVelRange;
|
|
8701
8821
|
if (zone.keyRange.min === 0 && zone.keyRange.max === 127) zone.keyRange.min = -1;
|
|
@@ -8825,6 +8945,7 @@ var BasicInstrument = class {
|
|
|
8825
8945
|
/**
|
|
8826
8946
|
* Unlinks the instrument from a given preset.
|
|
8827
8947
|
* @param preset The preset to unlink from.
|
|
8948
|
+
* @internal
|
|
8828
8949
|
*/
|
|
8829
8950
|
unlinkFrom(preset) {
|
|
8830
8951
|
const index = this.linkedTo.indexOf(preset);
|
|
@@ -9109,7 +9230,7 @@ function writeSF(bank, software, writeDefaultModulators, writeExtendedLimits, wr
|
|
|
9109
9230
|
*/
|
|
9110
9231
|
const infoArrays = [];
|
|
9111
9232
|
const writeSF2Info = (type, data) => {
|
|
9112
|
-
if (
|
|
9233
|
+
if (data === void 0) return;
|
|
9113
9234
|
infoArrays.push(...RIFFChunk.getParts(type, [getStringBytes(data, true, true)], rf64));
|
|
9114
9235
|
};
|
|
9115
9236
|
const info = bank.soundBankInfo;
|
|
@@ -9120,7 +9241,7 @@ function writeSF(bank, software, writeDefaultModulators, writeExtendedLimits, wr
|
|
|
9120
9241
|
infoArrays.push(RIFFChunk.write("ifil", ifilData, rf64));
|
|
9121
9242
|
}
|
|
9122
9243
|
writeSF2Info("isng", info.soundEngine);
|
|
9123
|
-
writeSF2Info("INAM", info.name);
|
|
9244
|
+
writeSF2Info("INAM", info.name ?? "");
|
|
9124
9245
|
writeSF2Info("irom", info.romInfo);
|
|
9125
9246
|
if (info.romVersion) {
|
|
9126
9247
|
const ifilData = new IndexedByteArray(4);
|
|
@@ -9507,6 +9628,9 @@ var DLSSample = class extends BasicSample {
|
|
|
9507
9628
|
if (this.rawData && !this.audioData) return "float32";
|
|
9508
9629
|
return super.storedForm;
|
|
9509
9630
|
}
|
|
9631
|
+
get canRedecode() {
|
|
9632
|
+
return this.rawData !== void 0 && !this.dataOverridden || super.canRedecode;
|
|
9633
|
+
}
|
|
9510
9634
|
getStoredData() {
|
|
9511
9635
|
if (this.rawData && !this.audioData) this.getAudioData();
|
|
9512
9636
|
return super.getStoredData();
|
|
@@ -9515,13 +9639,6 @@ var DLSSample = class extends BasicSample {
|
|
|
9515
9639
|
this.rawData = void 0;
|
|
9516
9640
|
super.setStoredData(stored);
|
|
9517
9641
|
}
|
|
9518
|
-
releaseDecoded() {
|
|
9519
|
-
if (this.rawData && this.audioData && !this.dataOverridden) {
|
|
9520
|
-
this.audioData = void 0;
|
|
9521
|
-
return true;
|
|
9522
|
-
}
|
|
9523
|
-
return super.releaseDecoded();
|
|
9524
|
-
}
|
|
9525
9642
|
releaseData() {
|
|
9526
9643
|
if (this.hasData) this.releasedForm = this.storedForm;
|
|
9527
9644
|
this.cachedLength = this.sampleLength;
|
|
@@ -9599,7 +9716,7 @@ var DownloadableSoundsSample = class DownloadableSoundsSample extends DLSVerifie
|
|
|
9599
9716
|
}
|
|
9600
9717
|
static fromSFSample(sample) {
|
|
9601
9718
|
const raw = sample.getRawData(false);
|
|
9602
|
-
const dlsSample = new DownloadableSoundsSample(1, 2, sample.sampleRate, new RIFFChunk("data", raw.length, new IndexedByteArray(raw.buffer)));
|
|
9719
|
+
const dlsSample = new DownloadableSoundsSample(1, 2, sample.sampleRate, new RIFFChunk("data", raw.length, new IndexedByteArray(raw.buffer, raw.byteOffset, raw.byteLength)));
|
|
9603
9720
|
dlsSample.name = sample.name;
|
|
9604
9721
|
dlsSample.waveSample = WaveSample.fromSFSample(sample);
|
|
9605
9722
|
return dlsSample;
|
|
@@ -10687,8 +10804,8 @@ var DownloadableSoundsInstrument = class DownloadableSoundsInstrument extends DL
|
|
|
10687
10804
|
SpessaLog.groupCollapsed(`%cWriting %c${this.name}%c...`, ConsoleColors.info, ConsoleColors.recognized, ConsoleColors.info);
|
|
10688
10805
|
const chunks = [this.writeHeader()];
|
|
10689
10806
|
const regionChunks = this.regions.flatMap((r) => r.write());
|
|
10690
|
-
|
|
10691
|
-
if (this.articulation.length > 0)
|
|
10807
|
+
for (const part of RIFFChunk.getParts("lrgn", regionChunks, false, true)) chunks.push(part);
|
|
10808
|
+
if (this.articulation.length > 0) for (const part of this.articulation.write()) chunks.push(part);
|
|
10692
10809
|
const inam = RIFFChunk.write("INAM", getStringBytes(this.name, true));
|
|
10693
10810
|
chunks.push(RIFFChunk.write("INFO", inam, false, true));
|
|
10694
10811
|
SpessaLog.groupEnd();
|
|
@@ -10973,7 +11090,7 @@ var DownloadableSounds = class DownloadableSounds extends DLSVerifier {
|
|
|
10973
11090
|
*/
|
|
10974
11091
|
const DEFAULT_SOUND_BANK_CLONE_OPTIONS = {
|
|
10975
11092
|
reuseByName: true,
|
|
10976
|
-
|
|
11093
|
+
reuseChildren: "name",
|
|
10977
11094
|
addToBank: true
|
|
10978
11095
|
};
|
|
10979
11096
|
const MAX_BASE_NAME_LENGTH = 37;
|
|
@@ -11020,6 +11137,17 @@ var SoundBankCloner = class {
|
|
|
11020
11137
|
this.bank = bank;
|
|
11021
11138
|
this.options = fillWithDefaults(options, DEFAULT_SOUND_BANK_CLONE_OPTIONS);
|
|
11022
11139
|
}
|
|
11140
|
+
get ownReuse() {
|
|
11141
|
+
return this.options.reuseByName ? "name" : "none";
|
|
11142
|
+
}
|
|
11143
|
+
/**
|
|
11144
|
+
* Gets the clone this session made of an element.
|
|
11145
|
+
* @param source The sample or instrument that was cloned.
|
|
11146
|
+
* @returns The clone, or undefined if the session has not cloned the element.
|
|
11147
|
+
*/
|
|
11148
|
+
getClone(source) {
|
|
11149
|
+
return source instanceof BasicSample ? this.sampleClones.get(source) : this.instrumentClones.get(source);
|
|
11150
|
+
}
|
|
11023
11151
|
/**
|
|
11024
11152
|
* Clones a sample and its stereo partner.
|
|
11025
11153
|
* The data is shared with the original, not copied.
|
|
@@ -11027,7 +11155,7 @@ var SoundBankCloner = class {
|
|
|
11027
11155
|
* @returns The clone, or the existing sample with the same name if it is reused.
|
|
11028
11156
|
*/
|
|
11029
11157
|
cloneSample(sample) {
|
|
11030
|
-
return this.copySample(sample, this.
|
|
11158
|
+
return this.copySample(sample, this.ownReuse);
|
|
11031
11159
|
}
|
|
11032
11160
|
/**
|
|
11033
11161
|
* Clones an instrument and the samples it uses.
|
|
@@ -11035,7 +11163,7 @@ var SoundBankCloner = class {
|
|
|
11035
11163
|
* @returns The clone, or the existing instrument with the same name if it is reused.
|
|
11036
11164
|
*/
|
|
11037
11165
|
cloneInstrument(instrument) {
|
|
11038
|
-
return this.copyInstrument(instrument, this.
|
|
11166
|
+
return this.copyInstrument(instrument, this.ownReuse);
|
|
11039
11167
|
}
|
|
11040
11168
|
/**
|
|
11041
11169
|
* Clones a preset and the instruments and samples it uses.
|
|
@@ -11044,10 +11172,8 @@ var SoundBankCloner = class {
|
|
|
11044
11172
|
*/
|
|
11045
11173
|
clonePreset(preset) {
|
|
11046
11174
|
const all = [...this.bank.presets, ...this.created.presets];
|
|
11047
|
-
|
|
11048
|
-
|
|
11049
|
-
if (existing) return existing;
|
|
11050
|
-
}
|
|
11175
|
+
const existing = this.findReused(preset, all, this.ownReuse);
|
|
11176
|
+
if (existing) return existing;
|
|
11051
11177
|
const newPreset = new BasicPreset(this.bank);
|
|
11052
11178
|
newPreset.name = this.getFreeName(preset.name, all);
|
|
11053
11179
|
newPreset.bankMSB = preset.bankMSB;
|
|
@@ -11058,7 +11184,7 @@ var SoundBankCloner = class {
|
|
|
11058
11184
|
newPreset.genre = preset.genre;
|
|
11059
11185
|
newPreset.morphology = preset.morphology;
|
|
11060
11186
|
newPreset.globalZone.copyFrom(preset.globalZone);
|
|
11061
|
-
for (const zone of preset.zones) newPreset.createZone(this.copyInstrument(zone.instrument, this.options.
|
|
11187
|
+
for (const zone of preset.zones) newPreset.createZone(this.copyInstrument(zone.instrument, this.options.reuseChildren)).copyFrom(zone);
|
|
11062
11188
|
this.created.presets.push(newPreset);
|
|
11063
11189
|
if (this.options.addToBank) this.bank.addPresets(newPreset);
|
|
11064
11190
|
return newPreset;
|
|
@@ -11067,14 +11193,12 @@ var SoundBankCloner = class {
|
|
|
11067
11193
|
const cloned = this.instrumentClones.get(instrument);
|
|
11068
11194
|
if (cloned) return cloned;
|
|
11069
11195
|
const all = [...this.bank.instruments, ...this.created.instruments];
|
|
11070
|
-
|
|
11071
|
-
|
|
11072
|
-
if (existing) return existing;
|
|
11073
|
-
}
|
|
11196
|
+
const existing = this.findReused(instrument, all, reuse);
|
|
11197
|
+
if (existing) return existing;
|
|
11074
11198
|
const newInstrument = new BasicInstrument();
|
|
11075
11199
|
newInstrument.name = this.getFreeName(instrument.name, all);
|
|
11076
11200
|
newInstrument.globalZone.copyFrom(instrument.globalZone);
|
|
11077
|
-
for (const zone of instrument.zones) newInstrument.createZone(this.copySample(zone.sample, this.options.
|
|
11201
|
+
for (const zone of instrument.zones) newInstrument.createZone(this.copySample(zone.sample, this.options.reuseChildren)).copyFrom(zone);
|
|
11078
11202
|
this.instrumentClones.set(instrument, newInstrument);
|
|
11079
11203
|
this.created.instruments.push(newInstrument);
|
|
11080
11204
|
if (this.options.addToBank) this.bank.addInstruments(newInstrument);
|
|
@@ -11084,10 +11208,8 @@ var SoundBankCloner = class {
|
|
|
11084
11208
|
const cloned = this.sampleClones.get(sample);
|
|
11085
11209
|
if (cloned) return cloned;
|
|
11086
11210
|
const all = [...this.bank.samples, ...this.created.samples];
|
|
11087
|
-
|
|
11088
|
-
|
|
11089
|
-
if (existing) return existing;
|
|
11090
|
-
}
|
|
11211
|
+
const existing = this.findReused(sample, all, reuse);
|
|
11212
|
+
if (existing) return existing;
|
|
11091
11213
|
const newSample = new BasicSample(this.getFreeName(sample.name, all), sample.sampleRate, sample.originalKey, sample.pitchCorrection, sample.sampleType, sample.loopStart, sample.loopEnd);
|
|
11092
11214
|
if (sample.hasData) newSample.setStoredData(sample.getStoredData());
|
|
11093
11215
|
else {
|
|
@@ -11101,10 +11223,20 @@ var SoundBankCloner = class {
|
|
|
11101
11223
|
if (linked) {
|
|
11102
11224
|
const newLinked = this.sampleClones.get(linked);
|
|
11103
11225
|
if (newLinked) newSample.setLinkedSample(newLinked, sample.sampleType);
|
|
11104
|
-
else this.copySample(linked,
|
|
11226
|
+
else this.copySample(linked, "none");
|
|
11105
11227
|
}
|
|
11106
11228
|
return newSample;
|
|
11107
11229
|
}
|
|
11230
|
+
/**
|
|
11231
|
+
* Finds the element to use instead of a copy, if the options allow one.
|
|
11232
|
+
*/
|
|
11233
|
+
findReused(element, elements, reuse) {
|
|
11234
|
+
switch (reuse) {
|
|
11235
|
+
case "none": return;
|
|
11236
|
+
case "identity": return elements.includes(element) ? element : void 0;
|
|
11237
|
+
case "name": return elements.find((e) => e.name === element.name);
|
|
11238
|
+
}
|
|
11239
|
+
}
|
|
11108
11240
|
getFreeName(name, elements) {
|
|
11109
11241
|
const taken = new Set(elements.map((e) => e.name));
|
|
11110
11242
|
if (!taken.has(name)) return name;
|
|
@@ -11270,7 +11402,7 @@ var BasicSoundBank = class BasicSoundBank {
|
|
|
11270
11402
|
const copied = new BasicSoundBank(bank.type);
|
|
11271
11403
|
const cloner = new SoundBankCloner(copied, {
|
|
11272
11404
|
reuseByName: false,
|
|
11273
|
-
|
|
11405
|
+
reuseChildren: "none"
|
|
11274
11406
|
});
|
|
11275
11407
|
for (const p of bank.presets) cloner.clonePreset(p);
|
|
11276
11408
|
copied.soundBankInfo = { ...bank.soundBankInfo };
|
|
@@ -11284,13 +11416,13 @@ var BasicSoundBank = class BasicSoundBank {
|
|
|
11284
11416
|
* @param presets The presets to add.
|
|
11285
11417
|
*/
|
|
11286
11418
|
addCompletePresets(presets) {
|
|
11287
|
-
this.
|
|
11419
|
+
this.addElements(this.presets, presets);
|
|
11288
11420
|
const instrumentList = [];
|
|
11289
11421
|
for (const preset of presets) for (const zone of preset.zones) if (zone.instrument && !instrumentList.includes(zone.instrument)) instrumentList.push(zone.instrument);
|
|
11290
|
-
this.
|
|
11422
|
+
this.addElements(this.instruments, instrumentList);
|
|
11291
11423
|
const sampleList = [];
|
|
11292
11424
|
for (const instrument of instrumentList) for (const zone of instrument.zones) if (zone.sample && !sampleList.includes(zone.sample)) sampleList.push(zone.sample);
|
|
11293
|
-
this.
|
|
11425
|
+
this.addElements(this.samples, sampleList);
|
|
11294
11426
|
}
|
|
11295
11427
|
/**
|
|
11296
11428
|
* Sets the sound bank's sample format _in place_.
|
|
@@ -11304,9 +11436,11 @@ var BasicSoundBank = class BasicSoundBank {
|
|
|
11304
11436
|
* @param options Options associated with setting the sample format.
|
|
11305
11437
|
* @returns The samples whose sample rate or loop points changed.
|
|
11306
11438
|
* Compressing resamples samples outside 8000 to 96000 Hz.
|
|
11439
|
+
* @throws Error if `rejectOnFailure` is set and one of the samples has failed to compress.
|
|
11307
11440
|
*/
|
|
11308
11441
|
async setSampleFormat(options) {
|
|
11309
11442
|
const changed = new Array();
|
|
11443
|
+
const failed = new Array();
|
|
11310
11444
|
let writtenCount = 0;
|
|
11311
11445
|
const format = options.format;
|
|
11312
11446
|
const progressFunc = options.progressFunction;
|
|
@@ -11320,7 +11454,7 @@ var BasicSoundBank = class BasicSoundBank {
|
|
|
11320
11454
|
const f = options.compressionFunction;
|
|
11321
11455
|
if (!f) throw new Error(`No compression function supplied but '${format}' was requested.`);
|
|
11322
11456
|
const { sampleRate, loopStart, loopEnd } = s;
|
|
11323
|
-
await s.compressSample(f);
|
|
11457
|
+
if (!await s.compressSample(f)) failed.push(s);
|
|
11324
11458
|
if (s.sampleRate !== sampleRate || s.loopStart !== loopStart || s.loopEnd !== loopEnd) changed.push(s);
|
|
11325
11459
|
}
|
|
11326
11460
|
}
|
|
@@ -11328,6 +11462,7 @@ var BasicSoundBank = class BasicSoundBank {
|
|
|
11328
11462
|
progressFunc?.(writtenCount / this.samples.length);
|
|
11329
11463
|
SpessaLog.info(`%cEncoded sample %c${writtenCount}. ${s.name}%c of %c${this.samples.length}%c. Compressed: %c${s.isCompressed}%c.`, ConsoleColors.info, ConsoleColors.recognized, ConsoleColors.info, ConsoleColors.recognized, ConsoleColors.info, s.isCompressed ? ConsoleColors.recognized : ConsoleColors.unrecognized, ConsoleColors.info);
|
|
11330
11464
|
}
|
|
11465
|
+
if (failed.length > 0 && options.format === "compressed" && options.rejectOnFailure) throw new Error(`Failed to compress ${failed.length} samples: ${failed.map((s) => s.name).join(", ")}`);
|
|
11331
11466
|
switch (format) {
|
|
11332
11467
|
default:
|
|
11333
11468
|
case "pcm":
|
|
@@ -11391,21 +11526,21 @@ var BasicSoundBank = class BasicSoundBank {
|
|
|
11391
11526
|
* @param presets The presets to add.
|
|
11392
11527
|
*/
|
|
11393
11528
|
addPresets(...presets) {
|
|
11394
|
-
this.
|
|
11529
|
+
this.addElements(this.presets, presets);
|
|
11395
11530
|
}
|
|
11396
11531
|
/**
|
|
11397
11532
|
* Adds {@link BasicInstrument}s to this sound bank.
|
|
11398
11533
|
* @param instruments The instruments to add.
|
|
11399
11534
|
*/
|
|
11400
11535
|
addInstruments(...instruments) {
|
|
11401
|
-
this.
|
|
11536
|
+
this.addElements(this.instruments, instruments);
|
|
11402
11537
|
}
|
|
11403
11538
|
/**
|
|
11404
11539
|
* Adds {@link BasicSample}s to this sound bank.
|
|
11405
11540
|
* @param samples The samples to add.
|
|
11406
11541
|
*/
|
|
11407
11542
|
addSamples(...samples) {
|
|
11408
|
-
this.
|
|
11543
|
+
this.addElements(this.samples, samples);
|
|
11409
11544
|
}
|
|
11410
11545
|
/**
|
|
11411
11546
|
* Clones a sample into this bank, together with its stereo partner.
|
|
@@ -11457,9 +11592,11 @@ var BasicSoundBank = class BasicSoundBank {
|
|
|
11457
11592
|
}
|
|
11458
11593
|
/**
|
|
11459
11594
|
* Updates internal values. Call after updating the preset list.
|
|
11595
|
+
* @param sort If the presets should be sorted. Set it to `false` to keep a custom order,
|
|
11596
|
+
* only the internal values (like {@link isXGBank}) are then recomputed.
|
|
11460
11597
|
*/
|
|
11461
|
-
flush() {
|
|
11462
|
-
this.presets.sort(MIDIPatchTools.compare.bind(MIDIPatchTools));
|
|
11598
|
+
flush(sort = true) {
|
|
11599
|
+
if (sort) this.presets.sort(MIDIPatchTools.compare.bind(MIDIPatchTools));
|
|
11463
11600
|
this.parseInternal();
|
|
11464
11601
|
}
|
|
11465
11602
|
/**
|
|
@@ -11604,6 +11741,9 @@ var BasicSoundBank = class BasicSoundBank {
|
|
|
11604
11741
|
this.instruments.length = 0;
|
|
11605
11742
|
this.samples.length = 0;
|
|
11606
11743
|
}
|
|
11744
|
+
addElements(list, elements) {
|
|
11745
|
+
for (const element of elements) list.push(element);
|
|
11746
|
+
}
|
|
11607
11747
|
parsingError(error) {
|
|
11608
11748
|
throw new Error(`SF parsing error: ${error} The file may be corrupted.`);
|
|
11609
11749
|
}
|
|
@@ -15405,7 +15545,6 @@ function setTimeToInternal(time, ticks = void 0) {
|
|
|
15405
15545
|
const channel = analyzed.channel + portOffset;
|
|
15406
15546
|
if (channel >= this.synth.midiChannels.length) break;
|
|
15407
15547
|
channels[channel] ??= createChannelStatus(channel);
|
|
15408
|
-
if (this._midiData.isMultiPort && track.channels.size === 0) break;
|
|
15409
15548
|
if (controller === MIDIControllers.resetAllControllers) {
|
|
15410
15549
|
resetAllControllers(channel);
|
|
15411
15550
|
break;
|
|
@@ -32584,6 +32723,148 @@ var DefaultGSDelay = class {
|
|
|
32584
32723
|
this.gainRight = this._levelRight / 127;
|
|
32585
32724
|
}
|
|
32586
32725
|
};
|
|
32726
|
+
// src/synthesizer/audio_engine/effects/gs/implementation/equalizer.ts
|
|
32727
|
+
const LOW_FREQUENCIES = [225, 425];
|
|
32728
|
+
const HIGH_FREQUENCIES = [3e3, 6e3];
|
|
32729
|
+
/**
|
|
32730
|
+
* The default GS Equalizer implementation for {@link SpessaSynthProcessor}.
|
|
32731
|
+
*
|
|
32732
|
+
* @group Synthesizer.GS Effects
|
|
32733
|
+
*/
|
|
32734
|
+
var DefaultGSEqualizer = class {
|
|
32735
|
+
sampleRate;
|
|
32736
|
+
lowB0 = 1;
|
|
32737
|
+
lowB1 = 0;
|
|
32738
|
+
lowA1 = 0;
|
|
32739
|
+
highB0 = 1;
|
|
32740
|
+
highB1 = 0;
|
|
32741
|
+
highA1 = 0;
|
|
32742
|
+
lowZL = 0;
|
|
32743
|
+
lowZR = 0;
|
|
32744
|
+
highZL = 0;
|
|
32745
|
+
highZR = 0;
|
|
32746
|
+
/**
|
|
32747
|
+
* Constructs a new default GS equalizer processor.
|
|
32748
|
+
* @param sampleRate The sample rate, in Hertz.
|
|
32749
|
+
*/
|
|
32750
|
+
constructor(sampleRate) {
|
|
32751
|
+
this.sampleRate = sampleRate;
|
|
32752
|
+
this.updateLow();
|
|
32753
|
+
this.updateHigh();
|
|
32754
|
+
}
|
|
32755
|
+
_lowFrequency = 0;
|
|
32756
|
+
get lowFrequency() {
|
|
32757
|
+
return this._lowFrequency;
|
|
32758
|
+
}
|
|
32759
|
+
set lowFrequency(value) {
|
|
32760
|
+
this._lowFrequency = value;
|
|
32761
|
+
this.updateLow();
|
|
32762
|
+
}
|
|
32763
|
+
_lowGain = 64;
|
|
32764
|
+
get lowGain() {
|
|
32765
|
+
return this._lowGain;
|
|
32766
|
+
}
|
|
32767
|
+
set lowGain(value) {
|
|
32768
|
+
this._lowGain = value;
|
|
32769
|
+
this.updateLow();
|
|
32770
|
+
}
|
|
32771
|
+
_highFrequency = 0;
|
|
32772
|
+
get highFrequency() {
|
|
32773
|
+
return this._highFrequency;
|
|
32774
|
+
}
|
|
32775
|
+
set highFrequency(value) {
|
|
32776
|
+
this._highFrequency = value;
|
|
32777
|
+
this.updateHigh();
|
|
32778
|
+
}
|
|
32779
|
+
_highGain = 64;
|
|
32780
|
+
get highGain() {
|
|
32781
|
+
return this._highGain;
|
|
32782
|
+
}
|
|
32783
|
+
set highGain(value) {
|
|
32784
|
+
this._highGain = value;
|
|
32785
|
+
this.updateHigh();
|
|
32786
|
+
}
|
|
32787
|
+
process(inputLeft, inputRight, outputLeft, outputRight, startIndex, sampleCount) {
|
|
32788
|
+
const { lowB0, lowB1, lowA1, highB0, highB1, highA1 } = this;
|
|
32789
|
+
let { lowZL, lowZR, highZL, highZR } = this;
|
|
32790
|
+
for (let i = 0; i < sampleCount; i++) {
|
|
32791
|
+
const o = startIndex + i;
|
|
32792
|
+
let x = inputLeft[i];
|
|
32793
|
+
let y = lowB0 * x + lowZL;
|
|
32794
|
+
lowZL = lowB1 * x - lowA1 * y;
|
|
32795
|
+
x = y;
|
|
32796
|
+
y = highB0 * x + highZL;
|
|
32797
|
+
highZL = highB1 * x - highA1 * y;
|
|
32798
|
+
outputLeft[o] += y;
|
|
32799
|
+
x = inputRight[i];
|
|
32800
|
+
y = lowB0 * x + lowZR;
|
|
32801
|
+
lowZR = lowB1 * x - lowA1 * y;
|
|
32802
|
+
x = y;
|
|
32803
|
+
y = highB0 * x + highZR;
|
|
32804
|
+
highZR = highB1 * x - highA1 * y;
|
|
32805
|
+
outputRight[o] += y;
|
|
32806
|
+
}
|
|
32807
|
+
this.lowZL = lowZL;
|
|
32808
|
+
this.lowZR = lowZR;
|
|
32809
|
+
this.highZL = highZL;
|
|
32810
|
+
this.highZR = highZR;
|
|
32811
|
+
}
|
|
32812
|
+
getSnapshot() {
|
|
32813
|
+
return {
|
|
32814
|
+
lowFrequency: this._lowFrequency,
|
|
32815
|
+
lowGain: this._lowGain,
|
|
32816
|
+
highFrequency: this._highFrequency,
|
|
32817
|
+
highGain: this._highGain
|
|
32818
|
+
};
|
|
32819
|
+
}
|
|
32820
|
+
applySnapshot(snapshot) {
|
|
32821
|
+
this.lowFrequency = snapshot.lowFrequency;
|
|
32822
|
+
this.lowGain = snapshot.lowGain;
|
|
32823
|
+
this.highFrequency = snapshot.highFrequency;
|
|
32824
|
+
this.highGain = snapshot.highGain;
|
|
32825
|
+
}
|
|
32826
|
+
getBand(frequency, gain) {
|
|
32827
|
+
const fc = Math.min(frequency, this.sampleRate * .45);
|
|
32828
|
+
const dB = Math.max(-12, Math.min(12, gain - 64));
|
|
32829
|
+
return {
|
|
32830
|
+
k: Math.tan(Math.PI * fc / this.sampleRate),
|
|
32831
|
+
g: 10 ** (Math.abs(dB) / 20),
|
|
32832
|
+
cut: dB < 0
|
|
32833
|
+
};
|
|
32834
|
+
}
|
|
32835
|
+
updateLow() {
|
|
32836
|
+
const { k, g, cut } = this.getBand(LOW_FREQUENCIES[this._lowFrequency & 1], this._lowGain);
|
|
32837
|
+
let b0 = 1 + k;
|
|
32838
|
+
let b1 = k - 1;
|
|
32839
|
+
let a0 = 1 + k / g;
|
|
32840
|
+
let a1 = k / g - 1;
|
|
32841
|
+
if (cut) [b0, b1, a0, a1] = [
|
|
32842
|
+
a0,
|
|
32843
|
+
a1,
|
|
32844
|
+
b0,
|
|
32845
|
+
b1
|
|
32846
|
+
];
|
|
32847
|
+
this.lowB0 = b0 / a0;
|
|
32848
|
+
this.lowB1 = b1 / a0;
|
|
32849
|
+
this.lowA1 = a1 / a0;
|
|
32850
|
+
}
|
|
32851
|
+
updateHigh() {
|
|
32852
|
+
const { k, g, cut } = this.getBand(HIGH_FREQUENCIES[this._highFrequency & 1], this._highGain);
|
|
32853
|
+
let b0 = g * (1 + k);
|
|
32854
|
+
let b1 = g * (k - 1);
|
|
32855
|
+
let a0 = 1 + g * k;
|
|
32856
|
+
let a1 = g * k - 1;
|
|
32857
|
+
if (cut) [b0, b1, a0, a1] = [
|
|
32858
|
+
a0,
|
|
32859
|
+
a1,
|
|
32860
|
+
b0,
|
|
32861
|
+
b1
|
|
32862
|
+
];
|
|
32863
|
+
this.highB0 = b0 / a0;
|
|
32864
|
+
this.highB1 = b1 / a0;
|
|
32865
|
+
this.highA1 = a1 / a0;
|
|
32866
|
+
}
|
|
32867
|
+
};
|
|
32587
32868
|
// src/synthesizer/audio_engine/effects/gs/implementation/dsp/gs_effect.ts
|
|
32588
32869
|
/**
|
|
32589
32870
|
* The number of EFX parameters (1-20 in the manuals, 0-19 here).
|
|
@@ -52388,6 +52669,7 @@ var DefaultGSInsertion = class {
|
|
|
52388
52669
|
sendLevelToReverb = DEFAULT_SEND_LEVEL_TO_REVERB;
|
|
52389
52670
|
sendLevelToChorus = 0;
|
|
52390
52671
|
sendLevelToDelay = 0;
|
|
52672
|
+
sendEQSwitch = true;
|
|
52391
52673
|
controlSource1 = GSInsertionControlSources.off;
|
|
52392
52674
|
controlDepth1 = DEFAULT_CONTROL_DEPTH;
|
|
52393
52675
|
controlSource2 = GSInsertionControlSources.off;
|
|
@@ -52457,6 +52739,7 @@ var DefaultGSInsertion = class {
|
|
|
52457
52739
|
this.sendLevelToReverb = DEFAULT_SEND_LEVEL_TO_REVERB;
|
|
52458
52740
|
this.sendLevelToChorus = 0;
|
|
52459
52741
|
this.sendLevelToDelay = 0;
|
|
52742
|
+
this.sendEQSwitch = true;
|
|
52460
52743
|
}
|
|
52461
52744
|
getType() {
|
|
52462
52745
|
return this.type;
|
|
@@ -52513,6 +52796,7 @@ var DefaultGSInsertion = class {
|
|
|
52513
52796
|
sendLevelToReverb: this.sendLevelToReverb,
|
|
52514
52797
|
sendLevelToChorus: this.sendLevelToChorus,
|
|
52515
52798
|
sendLevelToDelay: this.sendLevelToDelay,
|
|
52799
|
+
sendEQSwitch: this.sendEQSwitch,
|
|
52516
52800
|
controlSource1: this.controlSource1,
|
|
52517
52801
|
controlDepth1: this.controlDepth1,
|
|
52518
52802
|
controlSource2: this.controlSource2,
|
|
@@ -52533,6 +52817,7 @@ var DefaultGSInsertion = class {
|
|
|
52533
52817
|
this.sendLevelToReverb = snapshot.sendLevelToReverb;
|
|
52534
52818
|
this.sendLevelToChorus = snapshot.sendLevelToChorus;
|
|
52535
52819
|
this.sendLevelToDelay = snapshot.sendLevelToDelay;
|
|
52820
|
+
this.sendEQSwitch = snapshot.sendEQSwitch ?? true;
|
|
52536
52821
|
}
|
|
52537
52822
|
/**
|
|
52538
52823
|
* Sends the offsets of both control sources to the processor.
|
|
@@ -52662,15 +52947,19 @@ var SoundBankManager = class {
|
|
|
52662
52947
|
*/
|
|
52663
52948
|
userDrumSets;
|
|
52664
52949
|
presetListChangeCallback;
|
|
52950
|
+
bankRemovedCallback;
|
|
52665
52951
|
selectablePresetList = [];
|
|
52666
52952
|
/**
|
|
52667
52953
|
* @internal
|
|
52668
52954
|
* @param presetListChangeCallback Supplied by the parent synthesizer class,
|
|
52669
52955
|
* this is called whenever the preset list changes.
|
|
52670
52956
|
* The argument tells if the synthesizer should reset after updating its channels.
|
|
52957
|
+
* @param bankRemovedCallback Called when a sound bank leaves the manager (deleted, replaced or destroyed),
|
|
52958
|
+
* so the synthesizer can drop what it holds of it.
|
|
52671
52959
|
*/
|
|
52672
|
-
constructor(presetListChangeCallback) {
|
|
52960
|
+
constructor(presetListChangeCallback, bankRemovedCallback) {
|
|
52673
52961
|
this.presetListChangeCallback = presetListChangeCallback;
|
|
52962
|
+
this.bankRemovedCallback = bankRemovedCallback;
|
|
52674
52963
|
const resolvePatch = (patch) => this.getPreset(patch, this.systemGetter());
|
|
52675
52964
|
this.userDrumSets = [new UserDrumSet(64, "User Drum Set 1", resolvePatch), new UserDrumSet(65, "User Drum Set 2", resolvePatch)];
|
|
52676
52965
|
}
|
|
@@ -52719,13 +53008,16 @@ var SoundBankManager = class {
|
|
|
52719
53008
|
/**
|
|
52720
53009
|
* This method removes a sound bank with a given ID from the sound bank list.
|
|
52721
53010
|
* @param id The ID of the sound bank to delete.
|
|
53011
|
+
* @param reset If the synthesizer should reset (put every channel's controllers back to defaults) after the change.
|
|
53012
|
+
* Set it to `false` to keep a song playing, the channels then only update to the new presets.
|
|
52722
53013
|
* @throws Error An error if there's no sound bank with the given ID.
|
|
52723
53014
|
*/
|
|
52724
|
-
deleteSoundBank(id) {
|
|
53015
|
+
deleteSoundBank(id, reset = true) {
|
|
52725
53016
|
const index = this.soundBankList.findIndex((s) => s.id === id);
|
|
52726
53017
|
if (index === -1) throw new Error(`No sound bank with id "${id}"`);
|
|
52727
|
-
this.soundBankList.splice(index, 1);
|
|
52728
|
-
this.
|
|
53018
|
+
const [removed] = this.soundBankList.splice(index, 1);
|
|
53019
|
+
this.bankRemoved(removed.soundBank);
|
|
53020
|
+
this.generatePresetList(reset);
|
|
52729
53021
|
}
|
|
52730
53022
|
/**
|
|
52731
53023
|
* Adds a new sound bank with a given ID, or replaces an existing one.
|
|
@@ -52733,8 +53025,10 @@ var SoundBankManager = class {
|
|
|
52733
53025
|
* @param id The unique ID to assign to the sound bank.
|
|
52734
53026
|
* If a sound bank with this ID already exists, it will be replaced.
|
|
52735
53027
|
* @param bankOffset The bank offset of the sound bank. Offsets the bank MSB value of all presets in this sound bank.
|
|
53028
|
+
* @param reset If the synthesizer should reset (put every channel's controllers back to defaults) after the change.
|
|
53029
|
+
* Set it to `false` to keep a song playing, the channels then only update to the new presets.
|
|
52736
53030
|
*/
|
|
52737
|
-
addSoundBank(bank, id, bankOffset = 0) {
|
|
53031
|
+
addSoundBank(bank, id, bankOffset = 0, reset = true) {
|
|
52738
53032
|
const foundBank = this.soundBankList.find((s) => s.id === id);
|
|
52739
53033
|
if (foundBank === void 0) this.soundBankList.push({
|
|
52740
53034
|
id,
|
|
@@ -52742,10 +53036,12 @@ var SoundBankManager = class {
|
|
|
52742
53036
|
bankOffset
|
|
52743
53037
|
});
|
|
52744
53038
|
else {
|
|
53039
|
+
const old = foundBank.soundBank;
|
|
52745
53040
|
foundBank.soundBank = bank;
|
|
52746
53041
|
foundBank.bankOffset = bankOffset;
|
|
53042
|
+
this.bankRemoved(old);
|
|
52747
53043
|
}
|
|
52748
|
-
this.generatePresetList();
|
|
53044
|
+
this.generatePresetList(reset);
|
|
52749
53045
|
}
|
|
52750
53046
|
/**
|
|
52751
53047
|
* Rebuilds the preset list from the current sound banks.
|
|
@@ -52775,11 +53071,17 @@ var SoundBankManager = class {
|
|
|
52775
53071
|
* @internal
|
|
52776
53072
|
*/
|
|
52777
53073
|
destroy() {
|
|
52778
|
-
for (const s of this.soundBankList)
|
|
53074
|
+
for (const s of this.soundBankList) {
|
|
53075
|
+
this.bankRemovedCallback?.(s.soundBank);
|
|
53076
|
+
s.soundBank.destroySoundBank();
|
|
53077
|
+
}
|
|
52779
53078
|
this.soundBankList = [];
|
|
52780
53079
|
this.selectablePresetList = [];
|
|
52781
53080
|
this._presetList = [];
|
|
52782
53081
|
}
|
|
53082
|
+
bankRemoved(bank) {
|
|
53083
|
+
if (!this.soundBankList.some((s) => s.soundBank === bank)) this.bankRemovedCallback?.(bank);
|
|
53084
|
+
}
|
|
52783
53085
|
generatePresetList(resetSynth = true) {
|
|
52784
53086
|
const presetList = new Array();
|
|
52785
53087
|
const addedPresets = /* @__PURE__ */ new Set();
|
|
@@ -52928,7 +53230,7 @@ function renderVoice(voice, timeNow, sampleCount) {
|
|
|
52928
53230
|
let targetKey = voice.targetKey;
|
|
52929
53231
|
let cents = voice.pitchOffset + modulated[GeneratorTypes.fineTune] + this.octaveTuning[targetKey] + this.currentTuning;
|
|
52930
53232
|
let semitones = modulated[GeneratorTypes.coarseTune];
|
|
52931
|
-
const tuning = core.tunings[this.preset.program * 128 + voice.midiNote];
|
|
53233
|
+
const tuning = core.tunings[(this.preset?.program ?? this.patch.program) * 128 + voice.midiNote];
|
|
52932
53234
|
if (tuning !== -1) {
|
|
52933
53235
|
targetKey = Math.trunc(tuning);
|
|
52934
53236
|
semitones += tuning - targetKey;
|
|
@@ -54365,6 +54667,7 @@ const DEFAULT_CHANNEL_MIDI_PARAMETERS = {
|
|
|
54365
54667
|
randomPan: false,
|
|
54366
54668
|
assignMode: 2,
|
|
54367
54669
|
efxAssign: false,
|
|
54670
|
+
eqEnabled: true,
|
|
54368
54671
|
cc1: 16,
|
|
54369
54672
|
cc2: 17,
|
|
54370
54673
|
drumMap: 0,
|
|
@@ -55239,6 +55542,7 @@ const DEFAULT_GLOBAL_SYSTEM_PARAMETERS = {
|
|
|
55239
55542
|
xgChorusLock: false,
|
|
55240
55543
|
gsDelayGain: 1,
|
|
55241
55544
|
gsDelayLock: false,
|
|
55545
|
+
gsEqualizerLock: false,
|
|
55242
55546
|
xgVariationGain: 1,
|
|
55243
55547
|
xgVariationLock: false,
|
|
55244
55548
|
gsInsertionLock: false,
|
|
@@ -57251,6 +57555,27 @@ function rolandSystemExclusive(syx, channelOffset = 0) {
|
|
|
57251
57555
|
}
|
|
57252
57556
|
break;
|
|
57253
57557
|
}
|
|
57558
|
+
if (a2 === 2) {
|
|
57559
|
+
if (this.systemParameters.gsEqualizerLock) return;
|
|
57560
|
+
const parameter = [
|
|
57561
|
+
"lowFrequency",
|
|
57562
|
+
"lowGain",
|
|
57563
|
+
"highFrequency",
|
|
57564
|
+
"highGain"
|
|
57565
|
+
][a3];
|
|
57566
|
+
if (parameter === void 0) {
|
|
57567
|
+
SpessaLog.gsFail("Equalizer", [a3]);
|
|
57568
|
+
return;
|
|
57569
|
+
}
|
|
57570
|
+
this.gsEqualizerProcessor[parameter] = data;
|
|
57571
|
+
SpessaLog.gsInfo(`EQ ${parameter}`, data);
|
|
57572
|
+
this.callEvent("effectChange", {
|
|
57573
|
+
effect: "gsEqualizer",
|
|
57574
|
+
parameter,
|
|
57575
|
+
value: data
|
|
57576
|
+
});
|
|
57577
|
+
return;
|
|
57578
|
+
}
|
|
57254
57579
|
if (a2 === 3) {
|
|
57255
57580
|
if (this.systemParameters.gsInsertionLock) return;
|
|
57256
57581
|
const insertion = this.gsInsertionBlock;
|
|
@@ -57327,6 +57652,17 @@ function rolandSystemExclusive(syx, channelOffset = 0) {
|
|
|
57327
57652
|
});
|
|
57328
57653
|
return;
|
|
57329
57654
|
}
|
|
57655
|
+
case 31: {
|
|
57656
|
+
const on = data !== 0;
|
|
57657
|
+
insertion.sendEQSwitch = on;
|
|
57658
|
+
SpessaLog.gsInfo("EFX Send EQ Switch", on ? "ON" : "OFF");
|
|
57659
|
+
this.callEvent("effectChange", {
|
|
57660
|
+
effect: "gsInsertion",
|
|
57661
|
+
parameter: "sendEQSwitch",
|
|
57662
|
+
value: on
|
|
57663
|
+
});
|
|
57664
|
+
return;
|
|
57665
|
+
}
|
|
57330
57666
|
}
|
|
57331
57667
|
}
|
|
57332
57668
|
if (a2 >> 4 === 1) {
|
|
@@ -57513,6 +57849,12 @@ function rolandSystemExclusive(syx, channelOffset = 0) {
|
|
|
57513
57849
|
case 1:
|
|
57514
57850
|
ch.controllerChange(MIDIControllers.bankSelectLSB, data);
|
|
57515
57851
|
break;
|
|
57852
|
+
case 32: {
|
|
57853
|
+
const eq = data === 1;
|
|
57854
|
+
ch.setMIDIParameter("eqEnabled", eq);
|
|
57855
|
+
SpessaLog.gsInfo(`EQ for ${channel}`, eq ? "ON" : "OFF");
|
|
57856
|
+
break;
|
|
57857
|
+
}
|
|
57516
57858
|
case 34: {
|
|
57517
57859
|
const efx = data === 1;
|
|
57518
57860
|
ch.setMIDIParameter("efxAssign", efx);
|
|
@@ -58741,7 +59083,7 @@ var SoundFont2 = class extends BasicSoundBank {
|
|
|
58741
59083
|
}
|
|
58742
59084
|
}
|
|
58743
59085
|
for (const s of samples) s.name = s.name.trim();
|
|
58744
|
-
this.
|
|
59086
|
+
this.addElements(this.samples, samples);
|
|
58745
59087
|
const instrumentGenerators = readGenerators(igenChunk, isSFE);
|
|
58746
59088
|
const instrumentModulators = readModulators(imodChunk, isSFE);
|
|
58747
59089
|
const extendInstruments = xdtaChunk !== void 0 && !!xChunks.inst;
|
|
@@ -58758,7 +59100,7 @@ var SoundFont2 = class extends BasicSoundBank {
|
|
|
58758
59100
|
instruments.pop();
|
|
58759
59101
|
}
|
|
58760
59102
|
for (const i of instruments) i.name = i.name.trim();
|
|
58761
|
-
this.
|
|
59103
|
+
this.addElements(this.instruments, instruments);
|
|
58762
59104
|
const ibagIndexes = readZoneIndexes(ibagChunk);
|
|
58763
59105
|
if (xdtaChunk && xChunks.ibag) {
|
|
58764
59106
|
const extraIndexes = readZoneIndexes(xChunks.ibag);
|
|
@@ -58785,7 +59127,7 @@ var SoundFont2 = class extends BasicSoundBank {
|
|
|
58785
59127
|
presets.pop();
|
|
58786
59128
|
}
|
|
58787
59129
|
for (const p of presets) p.name = p.name.trim();
|
|
58788
|
-
this.
|
|
59130
|
+
this.addElements(this.presets, presets);
|
|
58789
59131
|
const pbagIndexes = readZoneIndexes(pbagChunk);
|
|
58790
59132
|
if (xdtaChunk && xChunks.pbag) {
|
|
58791
59133
|
const extraIndexes = readZoneIndexes(xChunks.pbag);
|
|
@@ -58860,6 +59202,7 @@ function applySnapshot(snapshot) {
|
|
|
58860
59202
|
this.gsReverbProcessor.applySnapshot(snapshot.gsReverbProcessor);
|
|
58861
59203
|
this.gsChorusProcessor.applySnapshot(snapshot.gsChorusProcessor);
|
|
58862
59204
|
this.gsDelayProcessor.applySnapshot(snapshot.gsDelayProcessor);
|
|
59205
|
+
if (snapshot.gsEqualizerProcessor) this.gsEqualizerProcessor.applySnapshot(snapshot.gsEqualizerProcessor);
|
|
58863
59206
|
this.gsInsertionBlock.applySnapshot(snapshot.gsInsertionBlock);
|
|
58864
59207
|
this.xgReverbBlock.applySnapshot(snapshot.xgReverbBlock);
|
|
58865
59208
|
this.xgChorusBlock.applySnapshot(snapshot.xgChorusBlock);
|
|
@@ -58888,6 +59231,7 @@ function getSynthesizerSnapshot() {
|
|
|
58888
59231
|
gsReverbProcessor: this.gsReverbProcessor.getSnapshot(),
|
|
58889
59232
|
gsChorusProcessor: this.gsChorusProcessor.getSnapshot(),
|
|
58890
59233
|
gsDelayProcessor: this.gsDelayProcessor.getSnapshot(),
|
|
59234
|
+
gsEqualizerProcessor: this.gsEqualizerProcessor.getSnapshot(),
|
|
58891
59235
|
gsInsertionBlock: this.gsInsertionBlock.getSnapshot(),
|
|
58892
59236
|
xgReverbBlock: this.xgReverbBlock.getSnapshot(),
|
|
58893
59237
|
xgChorusBlock: this.xgChorusBlock.getSnapshot(),
|
|
@@ -58914,7 +59258,6 @@ var DecodedSampleCache = class {
|
|
|
58914
59258
|
*/
|
|
58915
59259
|
totalBytes = 0;
|
|
58916
59260
|
entries = /* @__PURE__ */ new Map();
|
|
58917
|
-
unreleasable = /* @__PURE__ */ new WeakSet();
|
|
58918
59261
|
voiceCache;
|
|
58919
59262
|
/**
|
|
58920
59263
|
* @param budget The budget in bytes.
|
|
@@ -58935,7 +59278,7 @@ var DecodedSampleCache = class {
|
|
|
58935
59278
|
touch(sample, cacheKey) {
|
|
58936
59279
|
let entry = this.entries.get(sample);
|
|
58937
59280
|
if (entry) this.entries.delete(sample);
|
|
58938
|
-
if (
|
|
59281
|
+
if (!sample.canRedecode) {
|
|
58939
59282
|
if (entry) this.totalBytes -= entry.bytes;
|
|
58940
59283
|
return;
|
|
58941
59284
|
}
|
|
@@ -58950,16 +59293,29 @@ var DecodedSampleCache = class {
|
|
|
58950
59293
|
this.entries.set(sample, entry);
|
|
58951
59294
|
if (this.totalBytes > this.budget) this.evict(sample);
|
|
58952
59295
|
}
|
|
59296
|
+
/**
|
|
59297
|
+
* Stops tracking samples, for example when their sound bank is removed.
|
|
59298
|
+
* The samples are not released, the cache just stops holding them.
|
|
59299
|
+
* Voices cached from them are dropped too.
|
|
59300
|
+
* @param samples The samples to forget.
|
|
59301
|
+
*/
|
|
59302
|
+
forget(samples) {
|
|
59303
|
+
for (const sample of samples) {
|
|
59304
|
+
const entry = this.entries.get(sample);
|
|
59305
|
+
if (!entry) continue;
|
|
59306
|
+
this.entries.delete(sample);
|
|
59307
|
+
this.totalBytes -= entry.bytes;
|
|
59308
|
+
for (const key of entry.cacheKeys) this.voiceCache.delete(key);
|
|
59309
|
+
}
|
|
59310
|
+
}
|
|
58953
59311
|
evict(keep) {
|
|
58954
59312
|
for (const [sample, entry] of this.entries) {
|
|
58955
59313
|
if (this.totalBytes <= this.budget) return;
|
|
58956
59314
|
if (sample === keep) continue;
|
|
58957
59315
|
this.entries.delete(sample);
|
|
58958
59316
|
this.totalBytes -= entry.bytes;
|
|
58959
|
-
if (!sample.
|
|
58960
|
-
|
|
58961
|
-
continue;
|
|
58962
|
-
}
|
|
59317
|
+
if (!sample.canRedecode) continue;
|
|
59318
|
+
sample.releaseDecoded();
|
|
58963
59319
|
for (const key of entry.cacheKeys) this.voiceCache.delete(key);
|
|
58964
59320
|
}
|
|
58965
59321
|
}
|
|
@@ -59002,6 +59358,16 @@ const GS_CHORUS_MACRO_PARAMETERS = [
|
|
|
59002
59358
|
"sendLevelToDelay"
|
|
59003
59359
|
];
|
|
59004
59360
|
/**
|
|
59361
|
+
* The default GS equalizer parameters (SC-8850 manual p.236).
|
|
59362
|
+
*/
|
|
59363
|
+
const DEFAULT_GS_EQUALIZER = {
|
|
59364
|
+
lowFrequency: 0,
|
|
59365
|
+
lowGain: 64,
|
|
59366
|
+
highFrequency: 0,
|
|
59367
|
+
highGain: 64
|
|
59368
|
+
};
|
|
59369
|
+
const GS_EQUALIZER_PARAMETERS = Object.keys(DEFAULT_GS_EQUALIZER);
|
|
59370
|
+
/**
|
|
59005
59371
|
* The GS delay parameters set by the delay macro.
|
|
59006
59372
|
*/
|
|
59007
59373
|
const GS_DELAY_MACRO_PARAMETERS = [
|
|
@@ -59104,6 +59470,16 @@ var SpessaSynthProcessor = class {
|
|
|
59104
59470
|
*/
|
|
59105
59471
|
insertionInputR;
|
|
59106
59472
|
/**
|
|
59473
|
+
* The GS equalizer's left input buffer.
|
|
59474
|
+
* @internal
|
|
59475
|
+
*/
|
|
59476
|
+
equalizerInputL;
|
|
59477
|
+
/**
|
|
59478
|
+
* The GS equalizer's right input buffer.
|
|
59479
|
+
* @internal
|
|
59480
|
+
*/
|
|
59481
|
+
equalizerInputR;
|
|
59482
|
+
/**
|
|
59107
59483
|
* The GS reverb processor's input buffer.
|
|
59108
59484
|
* @internal
|
|
59109
59485
|
*/
|
|
@@ -59227,10 +59603,6 @@ var SpessaSynthProcessor = class {
|
|
|
59227
59603
|
*/
|
|
59228
59604
|
gsDelayActive = false;
|
|
59229
59605
|
/**
|
|
59230
|
-
* The sound bank manager, which manages all sound banks and presets.
|
|
59231
|
-
*/
|
|
59232
|
-
soundBankManager = new SoundBankManager(this.updatePresetList.bind(this));
|
|
59233
|
-
/**
|
|
59234
59606
|
* The sample rate, in Hertz.
|
|
59235
59607
|
*/
|
|
59236
59608
|
sampleRate;
|
|
@@ -59326,6 +59698,12 @@ var SpessaSynthProcessor = class {
|
|
|
59326
59698
|
*/
|
|
59327
59699
|
decodedSamples;
|
|
59328
59700
|
/**
|
|
59701
|
+
* The sound bank manager, which manages all sound banks and presets.
|
|
59702
|
+
*/
|
|
59703
|
+
soundBankManager = new SoundBankManager(this.updatePresetList.bind(this), (bank) => {
|
|
59704
|
+
this.decodedSamples.forget(bank.samples);
|
|
59705
|
+
});
|
|
59706
|
+
/**
|
|
59329
59707
|
* The synthesizer's GS reverb processor.
|
|
59330
59708
|
*
|
|
59331
59709
|
* Used when {@link GlobalMIDIParameter.system} is `gm` `gm2` or `gs`.
|
|
@@ -59347,6 +59725,13 @@ var SpessaSynthProcessor = class {
|
|
|
59347
59725
|
*/
|
|
59348
59726
|
gsDelayProcessor;
|
|
59349
59727
|
/**
|
|
59728
|
+
* The synthesizer's GS equalizer processor.
|
|
59729
|
+
*
|
|
59730
|
+
* Used when {@link GlobalMIDIParameter.system} is `gm` `gm2` or `gs`.
|
|
59731
|
+
* @internal
|
|
59732
|
+
*/
|
|
59733
|
+
gsEqualizerProcessor;
|
|
59734
|
+
/**
|
|
59350
59735
|
* The synthesizer's XG reverb block.
|
|
59351
59736
|
*
|
|
59352
59737
|
* Used when {@link GlobalMIDIParameter.system} is `xg`.
|
|
@@ -59430,6 +59815,7 @@ var SpessaSynthProcessor = class {
|
|
|
59430
59815
|
this.gsReverbProcessor = options.gsReverbProcessor ?? new DefaultGSReverb(sampleRate, bufSize);
|
|
59431
59816
|
this.gsChorusProcessor = options.gsChorusProcessor ?? new DefaultGSChorus(sampleRate, bufSize);
|
|
59432
59817
|
this.gsDelayProcessor = options.gsDelayProcessor ?? new DefaultGSDelay(sampleRate, bufSize);
|
|
59818
|
+
this.gsEqualizerProcessor = options.gsEqualizerProcessor ?? new DefaultGSEqualizer(sampleRate);
|
|
59433
59819
|
this.gsInsertionBlock = options.gsInsertionBlock ?? new DefaultGSInsertion(sampleRate, bufSize);
|
|
59434
59820
|
this.xgReverbBlock = options.xgReverbBlock ?? new DefaultXGReverb(sampleRate, bufSize);
|
|
59435
59821
|
this.xgChorusBlock = options.xgChorusBlock ?? new DefaultXGChorus(sampleRate, bufSize);
|
|
@@ -59439,6 +59825,8 @@ var SpessaSynthProcessor = class {
|
|
|
59439
59825
|
this.sharedVoiceSendBuffer = new VoiceSendBuffer(bufSize);
|
|
59440
59826
|
this.insertionInputL = new Float32Array(bufSize);
|
|
59441
59827
|
this.insertionInputR = new Float32Array(bufSize);
|
|
59828
|
+
this.equalizerInputL = new Float32Array(bufSize);
|
|
59829
|
+
this.equalizerInputR = new Float32Array(bufSize);
|
|
59442
59830
|
this.gsReverbInput = new Float32Array(bufSize);
|
|
59443
59831
|
this.gsChorusInput = new Float32Array(bufSize);
|
|
59444
59832
|
this.gsDelayInput = new Float32Array(bufSize);
|
|
@@ -59833,6 +60221,17 @@ var SpessaSynthProcessor = class {
|
|
|
59833
60221
|
this.setReverbMacro(4);
|
|
59834
60222
|
this.setChorusMacro(2);
|
|
59835
60223
|
this.setDelayMacro(0);
|
|
60224
|
+
if (!this.systemParameters.gsEqualizerLock) {
|
|
60225
|
+
const eq = this.gsEqualizerProcessor;
|
|
60226
|
+
for (const parameter of GS_EQUALIZER_PARAMETERS) {
|
|
60227
|
+
eq[parameter] = DEFAULT_GS_EQUALIZER[parameter];
|
|
60228
|
+
this.callEvent("effectChange", {
|
|
60229
|
+
effect: "gsEqualizer",
|
|
60230
|
+
parameter,
|
|
60231
|
+
value: eq[parameter]
|
|
60232
|
+
});
|
|
60233
|
+
}
|
|
60234
|
+
}
|
|
59836
60235
|
if (!this.systemParameters.gsInsertionLock) {
|
|
59837
60236
|
this.gsInsertionBlock.reset();
|
|
59838
60237
|
this.callEvent("effectChange", {
|
|
@@ -59989,10 +60388,10 @@ var SpessaSynthProcessor = class {
|
|
|
59989
60388
|
* │ │ │ Chorus │ │
|
|
59990
60389
|
* │ │ │ Processor ├──────────┐ │
|
|
59991
60390
|
* │ │ └─┬──────────┬──┘ │ │
|
|
59992
|
-
*
|
|
59993
|
-
*
|
|
59994
|
-
*
|
|
59995
|
-
*
|
|
60391
|
+
* ┌────┴────┐ │ │ │ │ │
|
|
60392
|
+
* │Equalizer│ │ │ │ │ │
|
|
60393
|
+
* │or Bypass│ │ │ │ │ │
|
|
60394
|
+
* └────┬────┘ │ │ │ │ │
|
|
59996
60395
|
* │ │ │
|
|
59997
60396
|
* │ │ │ ┌────────┴───────┐ ┌─┴─────┴────────┐
|
|
59998
60397
|
* │ └───┼>┤ Delay ├─>>┤ Reverb │
|
|
@@ -60021,6 +60420,7 @@ var SpessaSynthProcessor = class {
|
|
|
60021
60420
|
const isXG = this.midiParameters.system === "xg";
|
|
60022
60421
|
const variationInsertion = this.xgVariationBlock.insertionMode;
|
|
60023
60422
|
const fx = this.systemParameters.effectsEnabled;
|
|
60423
|
+
const eq = fx && !isXG;
|
|
60024
60424
|
if (isXG && fx) {
|
|
60025
60425
|
for (const channel of this.midiChannels) channel.xgInsertionAssigned = false;
|
|
60026
60426
|
for (const insertion of this.xgInsertionBlocks) {
|
|
@@ -60036,6 +60436,10 @@ var SpessaSynthProcessor = class {
|
|
|
60036
60436
|
this.insertionInputL.fill(0);
|
|
60037
60437
|
this.insertionInputR.fill(0);
|
|
60038
60438
|
}
|
|
60439
|
+
if (eq) {
|
|
60440
|
+
this.equalizerInputL.fill(0);
|
|
60441
|
+
this.equalizerInputR.fill(0);
|
|
60442
|
+
}
|
|
60039
60443
|
for (const c of this.midiChannels) {
|
|
60040
60444
|
c.clearVoiceCount();
|
|
60041
60445
|
if (c.outputDirty) {
|
|
@@ -60121,6 +60525,15 @@ var SpessaSynthProcessor = class {
|
|
|
60121
60525
|
}
|
|
60122
60526
|
continue;
|
|
60123
60527
|
}
|
|
60528
|
+
if (eq && midiParameters.eqEnabled) {
|
|
60529
|
+
const equalizerL = this.equalizerInputL;
|
|
60530
|
+
const equalizerR = this.equalizerInputR;
|
|
60531
|
+
for (let i = 0; i < sampleCount; i++) {
|
|
60532
|
+
equalizerL[i] += outputLeft[i];
|
|
60533
|
+
equalizerR[i] += outputRight[i];
|
|
60534
|
+
}
|
|
60535
|
+
continue;
|
|
60536
|
+
}
|
|
60124
60537
|
if (isXG && !variationInsertion) {
|
|
60125
60538
|
const gain = midiParameters.dryLevel / 127;
|
|
60126
60539
|
for (let i = 0; i < sampleCount; i++) {
|
|
@@ -60141,7 +60554,12 @@ var SpessaSynthProcessor = class {
|
|
|
60141
60554
|
this.xgChorusBlock.process(this.xgChorusInputL, this.xgChorusInputR, left, right, this.xgReverbInputL, this.xgReverbInputR, startIndex, sampleCount);
|
|
60142
60555
|
this.xgReverbBlock.process(this.xgReverbInputL, this.xgReverbInputR, left, right, startIndex, sampleCount);
|
|
60143
60556
|
} else {
|
|
60144
|
-
if (this.gsInsertionActive)
|
|
60557
|
+
if (this.gsInsertionActive) {
|
|
60558
|
+
const insertion = this.gsInsertionBlock;
|
|
60559
|
+
const toEqualizer = eq && insertion.sendEQSwitch;
|
|
60560
|
+
insertion.process(insertionInputL, insertionInputR, toEqualizer ? this.equalizerInputL : left, toEqualizer ? this.equalizerInputR : right, gsReverbInput, gsChorusInput, gsDelayInput, toEqualizer ? 0 : startIndex, sampleCount);
|
|
60561
|
+
}
|
|
60562
|
+
if (eq) this.gsEqualizerProcessor.process(this.equalizerInputL, this.equalizerInputR, left, right, startIndex, sampleCount);
|
|
60145
60563
|
this.gsChorusProcessor.process(gsChorusInput, left, right, gsReverbInput, gsDelayInput, startIndex, sampleCount);
|
|
60146
60564
|
if (this.gsDelayActive) this.gsDelayProcessor.process(gsDelayInput, left, right, gsReverbInput, startIndex, sampleCount);
|
|
60147
60565
|
this.gsReverbProcessor.process(gsReverbInput, left, right, startIndex, sampleCount);
|
|
@@ -60716,6 +61134,6 @@ var SpessaSynthProcessor = class {
|
|
|
60716
61134
|
return (isGMGSDrum ? 128 + patch.bankMSB : patch.bankMSB) + bankLSB * 256 + program * 32768 + 4194304 * midiNote + 536870912 * velocity;
|
|
60717
61135
|
}
|
|
60718
61136
|
};
|
|
60719
|
-
export { BasicInstrument, BasicInstrumentZone, BasicLinkedZone, BasicMIDI, BasicPreset, BasicPresetZone, BasicSample, BasicSoundBank, BasicZone, DEFAULT_CHANNEL_MIDI_PARAMETERS, DEFAULT_CHANNEL_SYSTEM_PARAMETERS, DEFAULT_DRUM_REVERB, DEFAULT_GLOBAL_MIDI_PARAMETERS, DEFAULT_GLOBAL_SYSTEM_PARAMETERS, DEFAULT_MIDI_CONTROLLERS, DEFAULT_SOUND_BANK_CLONE_OPTIONS, DEFAULT_SOUND_BANK_LOAD_OPTIONS, DEFAULT_SYNTH_MODE, DefaultGSChorus, DefaultGSDelay, DefaultGSInsertion, DefaultGSReverb, DefaultXGChorus, DefaultXGEffect, DefaultXGInsertion, DefaultXGReverb, DefaultXGSystemEffect, DefaultXGVariation, DrumParameterUtils, EmptySample, GENERATORS_AMOUNT, GSInsertionControlSources, Generator, GeneratorLimits, GeneratorTypes, INSTRUMENT_ONLY_GENERATORS, IndexedByteArray, InterpolationTypes, MAX_GENERATOR, MIDIBuilder, MIDIChannel, MIDIControllers, MIDIMessage, MIDIMessageTypes, MIDIPatchTools, MIDITrack, MIDIUtils, MIDI_DRUM_CHANNEL, Modulator, ModulatorControllerSources, ModulatorCurveTypes, ModulatorSource, ModulatorTransformTypes, NonRegisteredParameterTypesLSB, NonRegisteredParameterTypesMSB, RegisteredParameterTypes, SPESSASYNTH_GAIN_FACTOR, SPESSA_BUFSIZE, SampleTypes, SoundBankCloner, SoundBankLoader, SoundBankManager, SpessaLog, SpessaSynthCoreUtils, SpessaSynthProcessor, SpessaSynthSequencer, VOICE_CAP, audioToWav };
|
|
61137
|
+
export { BasicInstrument, BasicInstrumentZone, BasicLinkedZone, BasicMIDI, BasicPreset, BasicPresetZone, BasicSample, BasicSoundBank, BasicZone, DEFAULT_CHANNEL_MIDI_PARAMETERS, DEFAULT_CHANNEL_SYSTEM_PARAMETERS, DEFAULT_DRUM_REVERB, DEFAULT_GLOBAL_MIDI_PARAMETERS, DEFAULT_GLOBAL_SYSTEM_PARAMETERS, DEFAULT_MIDI_CONTROLLERS, DEFAULT_SOUND_BANK_CLONE_OPTIONS, DEFAULT_SOUND_BANK_LOAD_OPTIONS, DEFAULT_SYNTH_MODE, DefaultGSChorus, DefaultGSDelay, DefaultGSEqualizer, DefaultGSInsertion, DefaultGSReverb, DefaultXGChorus, DefaultXGEffect, DefaultXGInsertion, DefaultXGReverb, DefaultXGSystemEffect, DefaultXGVariation, DrumParameterUtils, EmptySample, GENERATORS_AMOUNT, GSInsertionControlSources, Generator, GeneratorLimits, GeneratorTypes, INSTRUMENT_ONLY_GENERATORS, IndexedByteArray, InterpolationTypes, MAX_GENERATOR, MIDIBuilder, MIDIChannel, MIDIControllers, MIDIMessage, MIDIMessageTypes, MIDIPatchTools, MIDITrack, MIDIUtils, MIDI_DRUM_CHANNEL, Modulator, ModulatorControllerSources, ModulatorCurveTypes, ModulatorSource, ModulatorTransformTypes, NonRegisteredParameterTypesLSB, NonRegisteredParameterTypesMSB, RegisteredParameterTypes, SPESSASYNTH_GAIN_FACTOR, SPESSA_BUFSIZE, SampleTypes, SoundBankCloner, SoundBankLoader, SoundBankManager, SpessaLog, SpessaSynthCoreUtils, SpessaSynthProcessor, SpessaSynthSequencer, VOICE_CAP, audioToWav, wavToAudio };
|
|
60720
61138
|
|
|
60721
61139
|
//# sourceMappingURL=index.js.map
|