spessasynth_core 4.4.7 → 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 +120 -41
- package/dist/index.js +292 -181
- 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
|
},
|
|
@@ -3997,6 +4033,12 @@ var MIDIUtils = class MIDIUtils {
|
|
|
3997
4033
|
controller: MIDIControllers.reverbDepth,
|
|
3998
4034
|
value
|
|
3999
4035
|
}];
|
|
4036
|
+
case 20: return [{
|
|
4037
|
+
type: "Controller Change",
|
|
4038
|
+
channel,
|
|
4039
|
+
controller: MIDIControllers.variationDepth,
|
|
4040
|
+
value
|
|
4041
|
+
}];
|
|
4000
4042
|
case 21: return [{
|
|
4001
4043
|
type: "Controller Change",
|
|
4002
4044
|
channel,
|
|
@@ -7337,6 +7379,22 @@ var BasicSample = class {
|
|
|
7337
7379
|
return this.linkedTo.length;
|
|
7338
7380
|
}
|
|
7339
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
|
+
/**
|
|
7340
7398
|
* Get raw data for writing the file, either a compressed bit stream or signed 16-bit little endian PCM data.
|
|
7341
7399
|
* @param allowVorbis if vorbis file data is allowed.
|
|
7342
7400
|
* @return either s16le or vorbis data.
|
|
@@ -7412,7 +7470,7 @@ var BasicSample = class {
|
|
|
7412
7470
|
* @returns True if the decoded data was dropped.
|
|
7413
7471
|
*/
|
|
7414
7472
|
releaseDecoded() {
|
|
7415
|
-
if (this.audioData &&
|
|
7473
|
+
if (this.audioData && this.canRedecode) {
|
|
7416
7474
|
this.audioData = void 0;
|
|
7417
7475
|
return true;
|
|
7418
7476
|
}
|
|
@@ -7444,12 +7502,13 @@ var BasicSample = class {
|
|
|
7444
7502
|
this.setAudioData(resampled, newSampleRate);
|
|
7445
7503
|
}
|
|
7446
7504
|
/**
|
|
7447
|
-
* Compresses the audio data
|
|
7505
|
+
* Compresses the audio data.
|
|
7448
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.
|
|
7449
7508
|
* @internal
|
|
7450
7509
|
*/
|
|
7451
7510
|
async compressSample(encodeVorbis) {
|
|
7452
|
-
if (this.isCompressed) return;
|
|
7511
|
+
if (this.isCompressed) return true;
|
|
7453
7512
|
try {
|
|
7454
7513
|
let audioData = this.getAudioData();
|
|
7455
7514
|
if (this.sampleRate < 8e3 || this.sampleRate > 96e3) {
|
|
@@ -7458,9 +7517,11 @@ var BasicSample = class {
|
|
|
7458
7517
|
}
|
|
7459
7518
|
const compressed = await encodeVorbis(audioData, this.sampleRate);
|
|
7460
7519
|
this.setCompressedData(compressed);
|
|
7520
|
+
return true;
|
|
7461
7521
|
} catch (error) {
|
|
7462
7522
|
SpessaLog.warn(`Failed to compress ${this.name}. Leaving as uncompressed!`, error);
|
|
7463
7523
|
this.compressedData = void 0;
|
|
7524
|
+
return false;
|
|
7464
7525
|
}
|
|
7465
7526
|
}
|
|
7466
7527
|
/**
|
|
@@ -7510,16 +7571,17 @@ var BasicSample = class {
|
|
|
7510
7571
|
}
|
|
7511
7572
|
}
|
|
7512
7573
|
/**
|
|
7513
|
-
* Links the sample to a given instrument
|
|
7514
|
-
* @param instrument the instrument to link to
|
|
7574
|
+
* Links the sample to a given instrument.
|
|
7575
|
+
* @param instrument the instrument to link to.
|
|
7515
7576
|
* @internal
|
|
7516
7577
|
*/
|
|
7517
7578
|
linkTo(instrument) {
|
|
7518
7579
|
this.linkedTo.push(instrument);
|
|
7519
7580
|
}
|
|
7520
7581
|
/**
|
|
7521
|
-
* Unlinks the sample from a given instrument
|
|
7522
|
-
* @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
|
|
7523
7585
|
*/
|
|
7524
7586
|
unlinkFrom(instrument) {
|
|
7525
7587
|
const index = this.linkedTo.indexOf(instrument);
|
|
@@ -7886,12 +7948,12 @@ var ModulatorSource = class ModulatorSource {
|
|
|
7886
7948
|
*/
|
|
7887
7949
|
curveType;
|
|
7888
7950
|
/**
|
|
7889
|
-
*
|
|
7890
|
-
* @param index
|
|
7891
|
-
* @param curveType
|
|
7892
|
-
* @param isCC
|
|
7893
|
-
* @param isBipolar
|
|
7894
|
-
* @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.
|
|
7895
7957
|
*/
|
|
7896
7958
|
constructor(index = ModulatorControllerSources.noController, curveType = ModulatorCurveTypes.linear, isCC = false, isBipolar = false, isNegative = false) {
|
|
7897
7959
|
this.isBipolar = isBipolar;
|
|
@@ -8359,14 +8421,13 @@ var BasicLinkedZone = class extends BasicZone {
|
|
|
8359
8421
|
}
|
|
8360
8422
|
/**
|
|
8361
8423
|
* Links the child to the parent, unless this zone was created without linking.
|
|
8362
|
-
* @
|
|
8424
|
+
* Use it to put a zone back after {@link unlinkChild}, for example when undoing a zone deletion.
|
|
8363
8425
|
*/
|
|
8364
8426
|
linkChild() {
|
|
8365
8427
|
if (this.linked) this.child.linkTo(this.parent);
|
|
8366
8428
|
}
|
|
8367
8429
|
/**
|
|
8368
8430
|
* Unlinks the child from the parent, unless this zone was created without linking.
|
|
8369
|
-
* @internal
|
|
8370
8431
|
*/
|
|
8371
8432
|
unlinkChild() {
|
|
8372
8433
|
if (this.linked) this.child.unlinkFrom(this.parent);
|
|
@@ -8754,7 +8815,7 @@ var BasicPreset = class BasicPreset {
|
|
|
8754
8815
|
}
|
|
8755
8816
|
}
|
|
8756
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));
|
|
8757
|
-
const zone = outputInstrument.createZone(instZone.sample);
|
|
8818
|
+
const zone = outputInstrument.createZone(instZone.sample, false);
|
|
8758
8819
|
zone.keyRange = instZoneKeyRange;
|
|
8759
8820
|
zone.velRange = instZoneVelRange;
|
|
8760
8821
|
if (zone.keyRange.min === 0 && zone.keyRange.max === 127) zone.keyRange.min = -1;
|
|
@@ -8884,6 +8945,7 @@ var BasicInstrument = class {
|
|
|
8884
8945
|
/**
|
|
8885
8946
|
* Unlinks the instrument from a given preset.
|
|
8886
8947
|
* @param preset The preset to unlink from.
|
|
8948
|
+
* @internal
|
|
8887
8949
|
*/
|
|
8888
8950
|
unlinkFrom(preset) {
|
|
8889
8951
|
const index = this.linkedTo.indexOf(preset);
|
|
@@ -9168,7 +9230,7 @@ function writeSF(bank, software, writeDefaultModulators, writeExtendedLimits, wr
|
|
|
9168
9230
|
*/
|
|
9169
9231
|
const infoArrays = [];
|
|
9170
9232
|
const writeSF2Info = (type, data) => {
|
|
9171
|
-
if (
|
|
9233
|
+
if (data === void 0) return;
|
|
9172
9234
|
infoArrays.push(...RIFFChunk.getParts(type, [getStringBytes(data, true, true)], rf64));
|
|
9173
9235
|
};
|
|
9174
9236
|
const info = bank.soundBankInfo;
|
|
@@ -9179,7 +9241,7 @@ function writeSF(bank, software, writeDefaultModulators, writeExtendedLimits, wr
|
|
|
9179
9241
|
infoArrays.push(RIFFChunk.write("ifil", ifilData, rf64));
|
|
9180
9242
|
}
|
|
9181
9243
|
writeSF2Info("isng", info.soundEngine);
|
|
9182
|
-
writeSF2Info("INAM", info.name);
|
|
9244
|
+
writeSF2Info("INAM", info.name ?? "");
|
|
9183
9245
|
writeSF2Info("irom", info.romInfo);
|
|
9184
9246
|
if (info.romVersion) {
|
|
9185
9247
|
const ifilData = new IndexedByteArray(4);
|
|
@@ -9566,6 +9628,9 @@ var DLSSample = class extends BasicSample {
|
|
|
9566
9628
|
if (this.rawData && !this.audioData) return "float32";
|
|
9567
9629
|
return super.storedForm;
|
|
9568
9630
|
}
|
|
9631
|
+
get canRedecode() {
|
|
9632
|
+
return this.rawData !== void 0 && !this.dataOverridden || super.canRedecode;
|
|
9633
|
+
}
|
|
9569
9634
|
getStoredData() {
|
|
9570
9635
|
if (this.rawData && !this.audioData) this.getAudioData();
|
|
9571
9636
|
return super.getStoredData();
|
|
@@ -9574,13 +9639,6 @@ var DLSSample = class extends BasicSample {
|
|
|
9574
9639
|
this.rawData = void 0;
|
|
9575
9640
|
super.setStoredData(stored);
|
|
9576
9641
|
}
|
|
9577
|
-
releaseDecoded() {
|
|
9578
|
-
if (this.rawData && this.audioData && !this.dataOverridden) {
|
|
9579
|
-
this.audioData = void 0;
|
|
9580
|
-
return true;
|
|
9581
|
-
}
|
|
9582
|
-
return super.releaseDecoded();
|
|
9583
|
-
}
|
|
9584
9642
|
releaseData() {
|
|
9585
9643
|
if (this.hasData) this.releasedForm = this.storedForm;
|
|
9586
9644
|
this.cachedLength = this.sampleLength;
|
|
@@ -9658,7 +9716,7 @@ var DownloadableSoundsSample = class DownloadableSoundsSample extends DLSVerifie
|
|
|
9658
9716
|
}
|
|
9659
9717
|
static fromSFSample(sample) {
|
|
9660
9718
|
const raw = sample.getRawData(false);
|
|
9661
|
-
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)));
|
|
9662
9720
|
dlsSample.name = sample.name;
|
|
9663
9721
|
dlsSample.waveSample = WaveSample.fromSFSample(sample);
|
|
9664
9722
|
return dlsSample;
|
|
@@ -10746,8 +10804,8 @@ var DownloadableSoundsInstrument = class DownloadableSoundsInstrument extends DL
|
|
|
10746
10804
|
SpessaLog.groupCollapsed(`%cWriting %c${this.name}%c...`, ConsoleColors.info, ConsoleColors.recognized, ConsoleColors.info);
|
|
10747
10805
|
const chunks = [this.writeHeader()];
|
|
10748
10806
|
const regionChunks = this.regions.flatMap((r) => r.write());
|
|
10749
|
-
|
|
10750
|
-
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);
|
|
10751
10809
|
const inam = RIFFChunk.write("INAM", getStringBytes(this.name, true));
|
|
10752
10810
|
chunks.push(RIFFChunk.write("INFO", inam, false, true));
|
|
10753
10811
|
SpessaLog.groupEnd();
|
|
@@ -11032,7 +11090,7 @@ var DownloadableSounds = class DownloadableSounds extends DLSVerifier {
|
|
|
11032
11090
|
*/
|
|
11033
11091
|
const DEFAULT_SOUND_BANK_CLONE_OPTIONS = {
|
|
11034
11092
|
reuseByName: true,
|
|
11035
|
-
|
|
11093
|
+
reuseChildren: "name",
|
|
11036
11094
|
addToBank: true
|
|
11037
11095
|
};
|
|
11038
11096
|
const MAX_BASE_NAME_LENGTH = 37;
|
|
@@ -11079,6 +11137,17 @@ var SoundBankCloner = class {
|
|
|
11079
11137
|
this.bank = bank;
|
|
11080
11138
|
this.options = fillWithDefaults(options, DEFAULT_SOUND_BANK_CLONE_OPTIONS);
|
|
11081
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
|
+
}
|
|
11082
11151
|
/**
|
|
11083
11152
|
* Clones a sample and its stereo partner.
|
|
11084
11153
|
* The data is shared with the original, not copied.
|
|
@@ -11086,7 +11155,7 @@ var SoundBankCloner = class {
|
|
|
11086
11155
|
* @returns The clone, or the existing sample with the same name if it is reused.
|
|
11087
11156
|
*/
|
|
11088
11157
|
cloneSample(sample) {
|
|
11089
|
-
return this.copySample(sample, this.
|
|
11158
|
+
return this.copySample(sample, this.ownReuse);
|
|
11090
11159
|
}
|
|
11091
11160
|
/**
|
|
11092
11161
|
* Clones an instrument and the samples it uses.
|
|
@@ -11094,7 +11163,7 @@ var SoundBankCloner = class {
|
|
|
11094
11163
|
* @returns The clone, or the existing instrument with the same name if it is reused.
|
|
11095
11164
|
*/
|
|
11096
11165
|
cloneInstrument(instrument) {
|
|
11097
|
-
return this.copyInstrument(instrument, this.
|
|
11166
|
+
return this.copyInstrument(instrument, this.ownReuse);
|
|
11098
11167
|
}
|
|
11099
11168
|
/**
|
|
11100
11169
|
* Clones a preset and the instruments and samples it uses.
|
|
@@ -11103,10 +11172,8 @@ var SoundBankCloner = class {
|
|
|
11103
11172
|
*/
|
|
11104
11173
|
clonePreset(preset) {
|
|
11105
11174
|
const all = [...this.bank.presets, ...this.created.presets];
|
|
11106
|
-
|
|
11107
|
-
|
|
11108
|
-
if (existing) return existing;
|
|
11109
|
-
}
|
|
11175
|
+
const existing = this.findReused(preset, all, this.ownReuse);
|
|
11176
|
+
if (existing) return existing;
|
|
11110
11177
|
const newPreset = new BasicPreset(this.bank);
|
|
11111
11178
|
newPreset.name = this.getFreeName(preset.name, all);
|
|
11112
11179
|
newPreset.bankMSB = preset.bankMSB;
|
|
@@ -11117,7 +11184,7 @@ var SoundBankCloner = class {
|
|
|
11117
11184
|
newPreset.genre = preset.genre;
|
|
11118
11185
|
newPreset.morphology = preset.morphology;
|
|
11119
11186
|
newPreset.globalZone.copyFrom(preset.globalZone);
|
|
11120
|
-
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);
|
|
11121
11188
|
this.created.presets.push(newPreset);
|
|
11122
11189
|
if (this.options.addToBank) this.bank.addPresets(newPreset);
|
|
11123
11190
|
return newPreset;
|
|
@@ -11126,14 +11193,12 @@ var SoundBankCloner = class {
|
|
|
11126
11193
|
const cloned = this.instrumentClones.get(instrument);
|
|
11127
11194
|
if (cloned) return cloned;
|
|
11128
11195
|
const all = [...this.bank.instruments, ...this.created.instruments];
|
|
11129
|
-
|
|
11130
|
-
|
|
11131
|
-
if (existing) return existing;
|
|
11132
|
-
}
|
|
11196
|
+
const existing = this.findReused(instrument, all, reuse);
|
|
11197
|
+
if (existing) return existing;
|
|
11133
11198
|
const newInstrument = new BasicInstrument();
|
|
11134
11199
|
newInstrument.name = this.getFreeName(instrument.name, all);
|
|
11135
11200
|
newInstrument.globalZone.copyFrom(instrument.globalZone);
|
|
11136
|
-
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);
|
|
11137
11202
|
this.instrumentClones.set(instrument, newInstrument);
|
|
11138
11203
|
this.created.instruments.push(newInstrument);
|
|
11139
11204
|
if (this.options.addToBank) this.bank.addInstruments(newInstrument);
|
|
@@ -11143,10 +11208,8 @@ var SoundBankCloner = class {
|
|
|
11143
11208
|
const cloned = this.sampleClones.get(sample);
|
|
11144
11209
|
if (cloned) return cloned;
|
|
11145
11210
|
const all = [...this.bank.samples, ...this.created.samples];
|
|
11146
|
-
|
|
11147
|
-
|
|
11148
|
-
if (existing) return existing;
|
|
11149
|
-
}
|
|
11211
|
+
const existing = this.findReused(sample, all, reuse);
|
|
11212
|
+
if (existing) return existing;
|
|
11150
11213
|
const newSample = new BasicSample(this.getFreeName(sample.name, all), sample.sampleRate, sample.originalKey, sample.pitchCorrection, sample.sampleType, sample.loopStart, sample.loopEnd);
|
|
11151
11214
|
if (sample.hasData) newSample.setStoredData(sample.getStoredData());
|
|
11152
11215
|
else {
|
|
@@ -11160,10 +11223,20 @@ var SoundBankCloner = class {
|
|
|
11160
11223
|
if (linked) {
|
|
11161
11224
|
const newLinked = this.sampleClones.get(linked);
|
|
11162
11225
|
if (newLinked) newSample.setLinkedSample(newLinked, sample.sampleType);
|
|
11163
|
-
else this.copySample(linked,
|
|
11226
|
+
else this.copySample(linked, "none");
|
|
11164
11227
|
}
|
|
11165
11228
|
return newSample;
|
|
11166
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
|
+
}
|
|
11167
11240
|
getFreeName(name, elements) {
|
|
11168
11241
|
const taken = new Set(elements.map((e) => e.name));
|
|
11169
11242
|
if (!taken.has(name)) return name;
|
|
@@ -11329,7 +11402,7 @@ var BasicSoundBank = class BasicSoundBank {
|
|
|
11329
11402
|
const copied = new BasicSoundBank(bank.type);
|
|
11330
11403
|
const cloner = new SoundBankCloner(copied, {
|
|
11331
11404
|
reuseByName: false,
|
|
11332
|
-
|
|
11405
|
+
reuseChildren: "none"
|
|
11333
11406
|
});
|
|
11334
11407
|
for (const p of bank.presets) cloner.clonePreset(p);
|
|
11335
11408
|
copied.soundBankInfo = { ...bank.soundBankInfo };
|
|
@@ -11343,13 +11416,13 @@ var BasicSoundBank = class BasicSoundBank {
|
|
|
11343
11416
|
* @param presets The presets to add.
|
|
11344
11417
|
*/
|
|
11345
11418
|
addCompletePresets(presets) {
|
|
11346
|
-
this.
|
|
11419
|
+
this.addElements(this.presets, presets);
|
|
11347
11420
|
const instrumentList = [];
|
|
11348
11421
|
for (const preset of presets) for (const zone of preset.zones) if (zone.instrument && !instrumentList.includes(zone.instrument)) instrumentList.push(zone.instrument);
|
|
11349
|
-
this.
|
|
11422
|
+
this.addElements(this.instruments, instrumentList);
|
|
11350
11423
|
const sampleList = [];
|
|
11351
11424
|
for (const instrument of instrumentList) for (const zone of instrument.zones) if (zone.sample && !sampleList.includes(zone.sample)) sampleList.push(zone.sample);
|
|
11352
|
-
this.
|
|
11425
|
+
this.addElements(this.samples, sampleList);
|
|
11353
11426
|
}
|
|
11354
11427
|
/**
|
|
11355
11428
|
* Sets the sound bank's sample format _in place_.
|
|
@@ -11363,9 +11436,11 @@ var BasicSoundBank = class BasicSoundBank {
|
|
|
11363
11436
|
* @param options Options associated with setting the sample format.
|
|
11364
11437
|
* @returns The samples whose sample rate or loop points changed.
|
|
11365
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.
|
|
11366
11440
|
*/
|
|
11367
11441
|
async setSampleFormat(options) {
|
|
11368
11442
|
const changed = new Array();
|
|
11443
|
+
const failed = new Array();
|
|
11369
11444
|
let writtenCount = 0;
|
|
11370
11445
|
const format = options.format;
|
|
11371
11446
|
const progressFunc = options.progressFunction;
|
|
@@ -11379,7 +11454,7 @@ var BasicSoundBank = class BasicSoundBank {
|
|
|
11379
11454
|
const f = options.compressionFunction;
|
|
11380
11455
|
if (!f) throw new Error(`No compression function supplied but '${format}' was requested.`);
|
|
11381
11456
|
const { sampleRate, loopStart, loopEnd } = s;
|
|
11382
|
-
await s.compressSample(f);
|
|
11457
|
+
if (!await s.compressSample(f)) failed.push(s);
|
|
11383
11458
|
if (s.sampleRate !== sampleRate || s.loopStart !== loopStart || s.loopEnd !== loopEnd) changed.push(s);
|
|
11384
11459
|
}
|
|
11385
11460
|
}
|
|
@@ -11387,6 +11462,7 @@ var BasicSoundBank = class BasicSoundBank {
|
|
|
11387
11462
|
progressFunc?.(writtenCount / this.samples.length);
|
|
11388
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);
|
|
11389
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(", ")}`);
|
|
11390
11466
|
switch (format) {
|
|
11391
11467
|
default:
|
|
11392
11468
|
case "pcm":
|
|
@@ -11450,21 +11526,21 @@ var BasicSoundBank = class BasicSoundBank {
|
|
|
11450
11526
|
* @param presets The presets to add.
|
|
11451
11527
|
*/
|
|
11452
11528
|
addPresets(...presets) {
|
|
11453
|
-
this.
|
|
11529
|
+
this.addElements(this.presets, presets);
|
|
11454
11530
|
}
|
|
11455
11531
|
/**
|
|
11456
11532
|
* Adds {@link BasicInstrument}s to this sound bank.
|
|
11457
11533
|
* @param instruments The instruments to add.
|
|
11458
11534
|
*/
|
|
11459
11535
|
addInstruments(...instruments) {
|
|
11460
|
-
this.
|
|
11536
|
+
this.addElements(this.instruments, instruments);
|
|
11461
11537
|
}
|
|
11462
11538
|
/**
|
|
11463
11539
|
* Adds {@link BasicSample}s to this sound bank.
|
|
11464
11540
|
* @param samples The samples to add.
|
|
11465
11541
|
*/
|
|
11466
11542
|
addSamples(...samples) {
|
|
11467
|
-
this.
|
|
11543
|
+
this.addElements(this.samples, samples);
|
|
11468
11544
|
}
|
|
11469
11545
|
/**
|
|
11470
11546
|
* Clones a sample into this bank, together with its stereo partner.
|
|
@@ -11516,9 +11592,11 @@ var BasicSoundBank = class BasicSoundBank {
|
|
|
11516
11592
|
}
|
|
11517
11593
|
/**
|
|
11518
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.
|
|
11519
11597
|
*/
|
|
11520
|
-
flush() {
|
|
11521
|
-
this.presets.sort(MIDIPatchTools.compare.bind(MIDIPatchTools));
|
|
11598
|
+
flush(sort = true) {
|
|
11599
|
+
if (sort) this.presets.sort(MIDIPatchTools.compare.bind(MIDIPatchTools));
|
|
11522
11600
|
this.parseInternal();
|
|
11523
11601
|
}
|
|
11524
11602
|
/**
|
|
@@ -11663,6 +11741,9 @@ var BasicSoundBank = class BasicSoundBank {
|
|
|
11663
11741
|
this.instruments.length = 0;
|
|
11664
11742
|
this.samples.length = 0;
|
|
11665
11743
|
}
|
|
11744
|
+
addElements(list, elements) {
|
|
11745
|
+
for (const element of elements) list.push(element);
|
|
11746
|
+
}
|
|
11666
11747
|
parsingError(error) {
|
|
11667
11748
|
throw new Error(`SF parsing error: ${error} The file may be corrupted.`);
|
|
11668
11749
|
}
|
|
@@ -15464,7 +15545,6 @@ function setTimeToInternal(time, ticks = void 0) {
|
|
|
15464
15545
|
const channel = analyzed.channel + portOffset;
|
|
15465
15546
|
if (channel >= this.synth.midiChannels.length) break;
|
|
15466
15547
|
channels[channel] ??= createChannelStatus(channel);
|
|
15467
|
-
if (this._midiData.isMultiPort && track.channels.size === 0) break;
|
|
15468
15548
|
if (controller === MIDIControllers.resetAllControllers) {
|
|
15469
15549
|
resetAllControllers(channel);
|
|
15470
15550
|
break;
|
|
@@ -52867,15 +52947,19 @@ var SoundBankManager = class {
|
|
|
52867
52947
|
*/
|
|
52868
52948
|
userDrumSets;
|
|
52869
52949
|
presetListChangeCallback;
|
|
52950
|
+
bankRemovedCallback;
|
|
52870
52951
|
selectablePresetList = [];
|
|
52871
52952
|
/**
|
|
52872
52953
|
* @internal
|
|
52873
52954
|
* @param presetListChangeCallback Supplied by the parent synthesizer class,
|
|
52874
52955
|
* this is called whenever the preset list changes.
|
|
52875
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.
|
|
52876
52959
|
*/
|
|
52877
|
-
constructor(presetListChangeCallback) {
|
|
52960
|
+
constructor(presetListChangeCallback, bankRemovedCallback) {
|
|
52878
52961
|
this.presetListChangeCallback = presetListChangeCallback;
|
|
52962
|
+
this.bankRemovedCallback = bankRemovedCallback;
|
|
52879
52963
|
const resolvePatch = (patch) => this.getPreset(patch, this.systemGetter());
|
|
52880
52964
|
this.userDrumSets = [new UserDrumSet(64, "User Drum Set 1", resolvePatch), new UserDrumSet(65, "User Drum Set 2", resolvePatch)];
|
|
52881
52965
|
}
|
|
@@ -52924,13 +53008,16 @@ var SoundBankManager = class {
|
|
|
52924
53008
|
/**
|
|
52925
53009
|
* This method removes a sound bank with a given ID from the sound bank list.
|
|
52926
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.
|
|
52927
53013
|
* @throws Error An error if there's no sound bank with the given ID.
|
|
52928
53014
|
*/
|
|
52929
|
-
deleteSoundBank(id) {
|
|
53015
|
+
deleteSoundBank(id, reset = true) {
|
|
52930
53016
|
const index = this.soundBankList.findIndex((s) => s.id === id);
|
|
52931
53017
|
if (index === -1) throw new Error(`No sound bank with id "${id}"`);
|
|
52932
|
-
this.soundBankList.splice(index, 1);
|
|
52933
|
-
this.
|
|
53018
|
+
const [removed] = this.soundBankList.splice(index, 1);
|
|
53019
|
+
this.bankRemoved(removed.soundBank);
|
|
53020
|
+
this.generatePresetList(reset);
|
|
52934
53021
|
}
|
|
52935
53022
|
/**
|
|
52936
53023
|
* Adds a new sound bank with a given ID, or replaces an existing one.
|
|
@@ -52938,8 +53025,10 @@ var SoundBankManager = class {
|
|
|
52938
53025
|
* @param id The unique ID to assign to the sound bank.
|
|
52939
53026
|
* If a sound bank with this ID already exists, it will be replaced.
|
|
52940
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.
|
|
52941
53030
|
*/
|
|
52942
|
-
addSoundBank(bank, id, bankOffset = 0) {
|
|
53031
|
+
addSoundBank(bank, id, bankOffset = 0, reset = true) {
|
|
52943
53032
|
const foundBank = this.soundBankList.find((s) => s.id === id);
|
|
52944
53033
|
if (foundBank === void 0) this.soundBankList.push({
|
|
52945
53034
|
id,
|
|
@@ -52947,10 +53036,12 @@ var SoundBankManager = class {
|
|
|
52947
53036
|
bankOffset
|
|
52948
53037
|
});
|
|
52949
53038
|
else {
|
|
53039
|
+
const old = foundBank.soundBank;
|
|
52950
53040
|
foundBank.soundBank = bank;
|
|
52951
53041
|
foundBank.bankOffset = bankOffset;
|
|
53042
|
+
this.bankRemoved(old);
|
|
52952
53043
|
}
|
|
52953
|
-
this.generatePresetList();
|
|
53044
|
+
this.generatePresetList(reset);
|
|
52954
53045
|
}
|
|
52955
53046
|
/**
|
|
52956
53047
|
* Rebuilds the preset list from the current sound banks.
|
|
@@ -52980,11 +53071,17 @@ var SoundBankManager = class {
|
|
|
52980
53071
|
* @internal
|
|
52981
53072
|
*/
|
|
52982
53073
|
destroy() {
|
|
52983
|
-
for (const s of this.soundBankList)
|
|
53074
|
+
for (const s of this.soundBankList) {
|
|
53075
|
+
this.bankRemovedCallback?.(s.soundBank);
|
|
53076
|
+
s.soundBank.destroySoundBank();
|
|
53077
|
+
}
|
|
52984
53078
|
this.soundBankList = [];
|
|
52985
53079
|
this.selectablePresetList = [];
|
|
52986
53080
|
this._presetList = [];
|
|
52987
53081
|
}
|
|
53082
|
+
bankRemoved(bank) {
|
|
53083
|
+
if (!this.soundBankList.some((s) => s.soundBank === bank)) this.bankRemovedCallback?.(bank);
|
|
53084
|
+
}
|
|
52988
53085
|
generatePresetList(resetSynth = true) {
|
|
52989
53086
|
const presetList = new Array();
|
|
52990
53087
|
const addedPresets = /* @__PURE__ */ new Set();
|
|
@@ -53133,7 +53230,7 @@ function renderVoice(voice, timeNow, sampleCount) {
|
|
|
53133
53230
|
let targetKey = voice.targetKey;
|
|
53134
53231
|
let cents = voice.pitchOffset + modulated[GeneratorTypes.fineTune] + this.octaveTuning[targetKey] + this.currentTuning;
|
|
53135
53232
|
let semitones = modulated[GeneratorTypes.coarseTune];
|
|
53136
|
-
const tuning = core.tunings[this.preset.program * 128 + voice.midiNote];
|
|
53233
|
+
const tuning = core.tunings[(this.preset?.program ?? this.patch.program) * 128 + voice.midiNote];
|
|
53137
53234
|
if (tuning !== -1) {
|
|
53138
53235
|
targetKey = Math.trunc(tuning);
|
|
53139
53236
|
semitones += tuning - targetKey;
|
|
@@ -58986,7 +59083,7 @@ var SoundFont2 = class extends BasicSoundBank {
|
|
|
58986
59083
|
}
|
|
58987
59084
|
}
|
|
58988
59085
|
for (const s of samples) s.name = s.name.trim();
|
|
58989
|
-
this.
|
|
59086
|
+
this.addElements(this.samples, samples);
|
|
58990
59087
|
const instrumentGenerators = readGenerators(igenChunk, isSFE);
|
|
58991
59088
|
const instrumentModulators = readModulators(imodChunk, isSFE);
|
|
58992
59089
|
const extendInstruments = xdtaChunk !== void 0 && !!xChunks.inst;
|
|
@@ -59003,7 +59100,7 @@ var SoundFont2 = class extends BasicSoundBank {
|
|
|
59003
59100
|
instruments.pop();
|
|
59004
59101
|
}
|
|
59005
59102
|
for (const i of instruments) i.name = i.name.trim();
|
|
59006
|
-
this.
|
|
59103
|
+
this.addElements(this.instruments, instruments);
|
|
59007
59104
|
const ibagIndexes = readZoneIndexes(ibagChunk);
|
|
59008
59105
|
if (xdtaChunk && xChunks.ibag) {
|
|
59009
59106
|
const extraIndexes = readZoneIndexes(xChunks.ibag);
|
|
@@ -59030,7 +59127,7 @@ var SoundFont2 = class extends BasicSoundBank {
|
|
|
59030
59127
|
presets.pop();
|
|
59031
59128
|
}
|
|
59032
59129
|
for (const p of presets) p.name = p.name.trim();
|
|
59033
|
-
this.
|
|
59130
|
+
this.addElements(this.presets, presets);
|
|
59034
59131
|
const pbagIndexes = readZoneIndexes(pbagChunk);
|
|
59035
59132
|
if (xdtaChunk && xChunks.pbag) {
|
|
59036
59133
|
const extraIndexes = readZoneIndexes(xChunks.pbag);
|
|
@@ -59161,7 +59258,6 @@ var DecodedSampleCache = class {
|
|
|
59161
59258
|
*/
|
|
59162
59259
|
totalBytes = 0;
|
|
59163
59260
|
entries = /* @__PURE__ */ new Map();
|
|
59164
|
-
unreleasable = /* @__PURE__ */ new WeakSet();
|
|
59165
59261
|
voiceCache;
|
|
59166
59262
|
/**
|
|
59167
59263
|
* @param budget The budget in bytes.
|
|
@@ -59182,7 +59278,7 @@ var DecodedSampleCache = class {
|
|
|
59182
59278
|
touch(sample, cacheKey) {
|
|
59183
59279
|
let entry = this.entries.get(sample);
|
|
59184
59280
|
if (entry) this.entries.delete(sample);
|
|
59185
|
-
if (
|
|
59281
|
+
if (!sample.canRedecode) {
|
|
59186
59282
|
if (entry) this.totalBytes -= entry.bytes;
|
|
59187
59283
|
return;
|
|
59188
59284
|
}
|
|
@@ -59197,16 +59293,29 @@ var DecodedSampleCache = class {
|
|
|
59197
59293
|
this.entries.set(sample, entry);
|
|
59198
59294
|
if (this.totalBytes > this.budget) this.evict(sample);
|
|
59199
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
|
+
}
|
|
59200
59311
|
evict(keep) {
|
|
59201
59312
|
for (const [sample, entry] of this.entries) {
|
|
59202
59313
|
if (this.totalBytes <= this.budget) return;
|
|
59203
59314
|
if (sample === keep) continue;
|
|
59204
59315
|
this.entries.delete(sample);
|
|
59205
59316
|
this.totalBytes -= entry.bytes;
|
|
59206
|
-
if (!sample.
|
|
59207
|
-
|
|
59208
|
-
continue;
|
|
59209
|
-
}
|
|
59317
|
+
if (!sample.canRedecode) continue;
|
|
59318
|
+
sample.releaseDecoded();
|
|
59210
59319
|
for (const key of entry.cacheKeys) this.voiceCache.delete(key);
|
|
59211
59320
|
}
|
|
59212
59321
|
}
|
|
@@ -59494,10 +59603,6 @@ var SpessaSynthProcessor = class {
|
|
|
59494
59603
|
*/
|
|
59495
59604
|
gsDelayActive = false;
|
|
59496
59605
|
/**
|
|
59497
|
-
* The sound bank manager, which manages all sound banks and presets.
|
|
59498
|
-
*/
|
|
59499
|
-
soundBankManager = new SoundBankManager(this.updatePresetList.bind(this));
|
|
59500
|
-
/**
|
|
59501
59606
|
* The sample rate, in Hertz.
|
|
59502
59607
|
*/
|
|
59503
59608
|
sampleRate;
|
|
@@ -59593,6 +59698,12 @@ var SpessaSynthProcessor = class {
|
|
|
59593
59698
|
*/
|
|
59594
59699
|
decodedSamples;
|
|
59595
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
|
+
/**
|
|
59596
59707
|
* The synthesizer's GS reverb processor.
|
|
59597
59708
|
*
|
|
59598
59709
|
* Used when {@link GlobalMIDIParameter.system} is `gm` `gm2` or `gs`.
|
|
@@ -61023,6 +61134,6 @@ var SpessaSynthProcessor = class {
|
|
|
61023
61134
|
return (isGMGSDrum ? 128 + patch.bankMSB : patch.bankMSB) + bankLSB * 256 + program * 32768 + 4194304 * midiNote + 536870912 * velocity;
|
|
61024
61135
|
}
|
|
61025
61136
|
};
|
|
61026
|
-
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 };
|
|
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 };
|
|
61027
61138
|
|
|
61028
61139
|
//# sourceMappingURL=index.js.map
|