spessasynth_core 4.3.10 → 4.3.12
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 +47 -48
- package/dist/index.js +225 -125
- package/dist/index.js.map +1 -1
- package/package.json +5 -3
package/dist/index.js
CHANGED
|
@@ -39,6 +39,29 @@ function writeBigEndian(number, bytesAmount) {
|
|
|
39
39
|
//#endregion
|
|
40
40
|
//#region src/utils/byte_functions/little_endian.ts
|
|
41
41
|
/**
|
|
42
|
+
* Reads the number as little endian from an IndexedByteArray. Uses BigInt to go above 32 bytes.
|
|
43
|
+
* @param dataArray the array to read from.
|
|
44
|
+
* @param bytesAmount the number of bytes to read.
|
|
45
|
+
* @returns the number.
|
|
46
|
+
*/
|
|
47
|
+
function readLE64Indexed(dataArray, bytesAmount) {
|
|
48
|
+
const res = readLE64(dataArray, bytesAmount, dataArray.currentIndex);
|
|
49
|
+
dataArray.currentIndex += bytesAmount;
|
|
50
|
+
return res;
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Reads the number as little endian. Uses BigInt to go above 32 bytes.
|
|
54
|
+
* @param dataArray the array to read from.
|
|
55
|
+
* @param bytesAmount the number of bytes to read.
|
|
56
|
+
* @param offset the offset to start reading at.
|
|
57
|
+
* @returns the number.
|
|
58
|
+
*/
|
|
59
|
+
function readLE64(dataArray, bytesAmount, offset = 0) {
|
|
60
|
+
let out = 0n;
|
|
61
|
+
for (let i = 0; i < bytesAmount; i++) out |= BigInt(dataArray[offset + i]) << BigInt(i * 8);
|
|
62
|
+
return Number(out);
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
42
65
|
* Reads the number as little endian from an IndexedByteArray.
|
|
43
66
|
* @param dataArray the array to read from.
|
|
44
67
|
* @param bytesAmount the number of bytes to read.
|
|
@@ -73,6 +96,7 @@ function writeLittleEndianIndexed(dataArray, number, byteTarget) {
|
|
|
73
96
|
}
|
|
74
97
|
/**
|
|
75
98
|
* Writes a WORD (SHORT)
|
|
99
|
+
* 16 bits.
|
|
76
100
|
*/
|
|
77
101
|
function writeWord(dataArray, word) {
|
|
78
102
|
dataArray[dataArray.currentIndex++] = word & 255;
|
|
@@ -80,11 +104,20 @@ function writeWord(dataArray, word) {
|
|
|
80
104
|
}
|
|
81
105
|
/**
|
|
82
106
|
* Writes a DWORD (INT)
|
|
107
|
+
* 32 bits.
|
|
83
108
|
*/
|
|
84
109
|
function writeDword(dataArray, dword) {
|
|
85
110
|
writeLittleEndianIndexed(dataArray, dword, 4);
|
|
86
111
|
}
|
|
87
112
|
/**
|
|
113
|
+
* Writes a QWORD (LONG)
|
|
114
|
+
* 64 bits.
|
|
115
|
+
*/
|
|
116
|
+
function writeQword(dataArray, qword) {
|
|
117
|
+
const qb = BigInt(qword);
|
|
118
|
+
for (let i = 0n; i < 8n; i++) dataArray[dataArray.currentIndex++] = Number(qb >> i * 8n & 255n);
|
|
119
|
+
}
|
|
120
|
+
/**
|
|
88
121
|
* Reads two bytes as a signed short.
|
|
89
122
|
*/
|
|
90
123
|
function signedInt16(byte1, byte2) {
|
|
@@ -525,44 +558,49 @@ var RIFFChunk = class RIFFChunk {
|
|
|
525
558
|
*/
|
|
526
559
|
data;
|
|
527
560
|
/**
|
|
561
|
+
* The size of the chunk's header in bytes.
|
|
562
|
+
* This varies for 32-bit and 64-bit RIFF chunks.
|
|
563
|
+
*/
|
|
564
|
+
headerSize;
|
|
565
|
+
/**
|
|
528
566
|
* Creates a new RIFF chunk.
|
|
529
567
|
*/
|
|
530
|
-
constructor(header, size, data) {
|
|
568
|
+
constructor(header, size, data, headerSize = 8) {
|
|
531
569
|
this.header = header;
|
|
532
570
|
this.size = size;
|
|
533
571
|
this.data = data;
|
|
572
|
+
this.headerSize = headerSize;
|
|
534
573
|
}
|
|
535
574
|
/**
|
|
536
575
|
* Reads a RIFF chunk from an array.
|
|
537
576
|
* @param dataArray the array to read from.
|
|
577
|
+
* @param rf64 if the chunk uses a 64-bit size.
|
|
538
578
|
* @param readData if the data should be read as well.
|
|
539
|
-
* @param forceShift if the index should be shifted to the end of the chunk even if the data has not been read.
|
|
540
579
|
*/
|
|
541
|
-
static read(dataArray,
|
|
580
|
+
static read(dataArray, rf64 = false, readData = true) {
|
|
542
581
|
const header = readBinaryStringIndexed(dataArray, 4);
|
|
543
|
-
let size = readLittleEndianIndexed(dataArray, 4);
|
|
582
|
+
let size = rf64 ? readLE64Indexed(dataArray, 8) : readLittleEndianIndexed(dataArray, 4);
|
|
544
583
|
if (header === "") size = 0;
|
|
545
584
|
const chunkData = readData ? dataArray.slice(dataArray.currentIndex, dataArray.currentIndex + size) : new IndexedByteArray(0);
|
|
546
|
-
if (readData
|
|
585
|
+
if (readData) {
|
|
547
586
|
dataArray.currentIndex += size;
|
|
548
587
|
if (size % 2 !== 0) dataArray.currentIndex++;
|
|
549
588
|
}
|
|
550
|
-
return new RIFFChunk(header, size, chunkData);
|
|
589
|
+
return new RIFFChunk(header, size, chunkData, rf64 ? 12 : 8);
|
|
551
590
|
}
|
|
552
591
|
/**
|
|
553
592
|
* Writes a RIFF chunk correctly.
|
|
554
593
|
* @param header the fourCC code of the header.
|
|
555
594
|
* @param data the binary chunk data.
|
|
556
|
-
* @param addZeroByte if a zero byte should be at the end of the chunk's data.
|
|
557
595
|
* @param isList if a "LIST" should be set as the chunk type and the actual type should be written at the start of the data.
|
|
596
|
+
* @param rf64 if the chunk uses a 64-bit size.
|
|
558
597
|
* @returns the binary data.
|
|
559
598
|
*/
|
|
560
|
-
static write(header, data,
|
|
599
|
+
static write(header, data, rf64 = false, isList = false) {
|
|
561
600
|
if (header.length !== 4) throw new Error(`Invalid header length: ${header}`);
|
|
562
|
-
let dataStartOffset = 8;
|
|
601
|
+
let dataStartOffset = rf64 ? 12 : 8;
|
|
563
602
|
let headerWritten = header;
|
|
564
|
-
|
|
565
|
-
if (addZeroByte) dataLength++;
|
|
603
|
+
const dataLength = data.length;
|
|
566
604
|
let writtenSize = dataLength;
|
|
567
605
|
if (isList) {
|
|
568
606
|
dataStartOffset += 4;
|
|
@@ -573,7 +611,8 @@ var RIFFChunk = class RIFFChunk {
|
|
|
573
611
|
if (finalSize % 2 !== 0) finalSize++;
|
|
574
612
|
const outArray = new IndexedByteArray(finalSize);
|
|
575
613
|
writeBinaryStringIndexed(outArray, headerWritten);
|
|
576
|
-
|
|
614
|
+
if (rf64) writeQword(outArray, writtenSize);
|
|
615
|
+
else writeDword(outArray, writtenSize);
|
|
577
616
|
if (isList) writeBinaryStringIndexed(outArray, header);
|
|
578
617
|
outArray.set(data, dataStartOffset);
|
|
579
618
|
return outArray;
|
|
@@ -586,18 +625,25 @@ var RIFFChunk = class RIFFChunk {
|
|
|
586
625
|
* @param header the fourCC code of the header.
|
|
587
626
|
* @param chunks binary chunk data parts, will be combined in order.
|
|
588
627
|
* @param isList if a "LIST" should be set as the chunk type and the actual type should be written at the start of the data.
|
|
628
|
+
* @param rf64 if the chunk uses a 64-bit size.
|
|
589
629
|
* @returns the chunk as binary blobs.
|
|
590
630
|
*/
|
|
591
|
-
static getParts(header, chunks, isList = false) {
|
|
631
|
+
static getParts(header, chunks, rf64 = false, isList = false) {
|
|
592
632
|
let headerWritten = header;
|
|
593
633
|
let totalSize = chunks.reduce((len, c) => c.length + len, 0);
|
|
594
634
|
if (isList) {
|
|
595
635
|
totalSize += 4;
|
|
596
636
|
headerWritten = "LIST";
|
|
597
637
|
}
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
638
|
+
let sizeBytes;
|
|
639
|
+
if (rf64) {
|
|
640
|
+
sizeBytes = new IndexedByteArray(8);
|
|
641
|
+
writeQword(sizeBytes, totalSize);
|
|
642
|
+
} else {
|
|
643
|
+
sizeBytes = new IndexedByteArray(4);
|
|
644
|
+
writeDword(sizeBytes, totalSize);
|
|
645
|
+
}
|
|
646
|
+
const parts = [getStringBytes(headerWritten), sizeBytes];
|
|
601
647
|
if (isList) parts.push(getStringBytes(header));
|
|
602
648
|
parts.push(...chunks);
|
|
603
649
|
if (totalSize % 2 !== 0) parts.push(new Uint8Array(1));
|
|
@@ -609,10 +655,11 @@ var RIFFChunk = class RIFFChunk {
|
|
|
609
655
|
* @param header the fourCC code of the header.
|
|
610
656
|
* @param chunks binary chunk data parts, will be combined in order.
|
|
611
657
|
* @param isList if a "LIST" should be set as the chunk type and the actual type should be written at the start of the data.
|
|
658
|
+
* @param rf64 if the chunk uses a 64-bit size.
|
|
612
659
|
* @returns the binary data.
|
|
613
660
|
*/
|
|
614
|
-
static writeParts(header, chunks, isList = false) {
|
|
615
|
-
let dataOffset = 8;
|
|
661
|
+
static writeParts(header, chunks, rf64 = false, isList = false) {
|
|
662
|
+
let dataOffset = rf64 ? 12 : 8;
|
|
616
663
|
let headerWritten = header;
|
|
617
664
|
const dataLength = chunks.reduce((len, c) => c.length + len, 0);
|
|
618
665
|
let writtenSize = dataLength;
|
|
@@ -625,7 +672,8 @@ var RIFFChunk = class RIFFChunk {
|
|
|
625
672
|
if (finalSize % 2 !== 0) finalSize++;
|
|
626
673
|
const outArray = new IndexedByteArray(finalSize);
|
|
627
674
|
writeBinaryStringIndexed(outArray, headerWritten);
|
|
628
|
-
|
|
675
|
+
if (rf64) writeQword(outArray, writtenSize);
|
|
676
|
+
else writeDword(outArray, writtenSize);
|
|
629
677
|
if (isList) writeBinaryStringIndexed(outArray, header);
|
|
630
678
|
for (const c of chunks) {
|
|
631
679
|
outArray.set(c, dataOffset);
|
|
@@ -684,12 +732,12 @@ function audioToWav(audioData, sampleRate, options = DEFAULT_WAV_WRITE_OPTIONS)
|
|
|
684
732
|
const infoOn = Object.keys(metadata).length > 0;
|
|
685
733
|
if (infoOn) {
|
|
686
734
|
const encoder = new TextEncoder();
|
|
687
|
-
const infoChunks = [RIFFChunk.
|
|
688
|
-
if (metadata.artist) infoChunks.push(RIFFChunk.
|
|
689
|
-
if (metadata.album) infoChunks.push(RIFFChunk.
|
|
690
|
-
if (metadata.genre) infoChunks.push(RIFFChunk.
|
|
691
|
-
if (metadata.title) infoChunks.push(RIFFChunk.
|
|
692
|
-
infoChunk = RIFFChunk.writeParts("INFO", infoChunks, true);
|
|
735
|
+
const infoChunks = [RIFFChunk.writeParts("ICMT", [encoder.encode("Created with SpessaSynth"), [0]])];
|
|
736
|
+
if (metadata.artist) infoChunks.push(RIFFChunk.writeParts("IART", [encoder.encode(metadata.artist), [0]]));
|
|
737
|
+
if (metadata.album) infoChunks.push(RIFFChunk.writeParts("IPRD", [encoder.encode(metadata.album), [0]]));
|
|
738
|
+
if (metadata.genre) infoChunks.push(RIFFChunk.writeParts("IGNR", [encoder.encode(metadata.genre), [0]]));
|
|
739
|
+
if (metadata.title) infoChunks.push(RIFFChunk.writeParts("INAM", [encoder.encode(metadata.title), [0]]));
|
|
740
|
+
infoChunk = RIFFChunk.writeParts("INFO", infoChunks, false, true);
|
|
693
741
|
}
|
|
694
742
|
let cueChunk = new IndexedByteArray(0);
|
|
695
743
|
const cueOn = loop?.end !== void 0 && loop?.start !== void 0;
|
|
@@ -2588,7 +2636,7 @@ function writeRMIDIInternal(mid, soundBankBinary, options) {
|
|
|
2588
2636
|
return RIFFChunk.writeParts("RIFF", [
|
|
2589
2637
|
getStringBytes("RMID"),
|
|
2590
2638
|
RIFFChunk.write("data", newMid),
|
|
2591
|
-
RIFFChunk.writeParts("INFO", infoContent, true),
|
|
2639
|
+
RIFFChunk.writeParts("INFO", infoContent, false, true),
|
|
2592
2640
|
new IndexedByteArray(soundBankBinary)
|
|
2593
2641
|
]).buffer;
|
|
2594
2642
|
}
|
|
@@ -3651,7 +3699,7 @@ function parseRMIDIInternal(outputMIDI, binaryData, fileName) {
|
|
|
3651
3699
|
let foundDBNK = false;
|
|
3652
3700
|
while (binaryData.currentIndex < binaryData.length) {
|
|
3653
3701
|
const startIndex = binaryData.currentIndex;
|
|
3654
|
-
const currentChunk = RIFFChunk.read(binaryData
|
|
3702
|
+
const currentChunk = RIFFChunk.read(binaryData);
|
|
3655
3703
|
if (currentChunk.header === "RIFF") {
|
|
3656
3704
|
const type = readBinaryStringIndexed(currentChunk.data, 4).toLowerCase();
|
|
3657
3705
|
if (type === "sfbk" || type === "sfpk" || type === "dls ") {
|
|
@@ -3664,7 +3712,7 @@ function parseRMIDIInternal(outputMIDI, binaryData, fileName) {
|
|
|
3664
3712
|
if (readBinaryStringIndexed(currentChunk.data, 4) === "INFO") {
|
|
3665
3713
|
SpessaLog.info("%cFound RMIDI INFO chunk!", ConsoleColors.recognized);
|
|
3666
3714
|
while (currentChunk.data.currentIndex < currentChunk.size) {
|
|
3667
|
-
const infoChunk = RIFFChunk.read(currentChunk.data
|
|
3715
|
+
const infoChunk = RIFFChunk.read(currentChunk.data);
|
|
3668
3716
|
const headerTyped = infoChunk.header;
|
|
3669
3717
|
const infoData = infoChunk.data;
|
|
3670
3718
|
switch (headerTyped) {
|
|
@@ -8605,7 +8653,7 @@ var BasicSample = class {
|
|
|
8605
8653
|
return decoded;
|
|
8606
8654
|
} catch (error) {
|
|
8607
8655
|
SpessaLog.warn(`Error decoding sample ${this.name}: ${error}`);
|
|
8608
|
-
return new Float32Array(this.loopEnd
|
|
8656
|
+
return new Float32Array(this.loopEnd);
|
|
8609
8657
|
}
|
|
8610
8658
|
}
|
|
8611
8659
|
};
|
|
@@ -8954,14 +9002,19 @@ var BasicPreset = class BasicPreset {
|
|
|
8954
9002
|
* Writes the SF2 header
|
|
8955
9003
|
* @param phdrData
|
|
8956
9004
|
* @param index
|
|
9005
|
+
* @param writeLSB
|
|
9006
|
+
* @internal
|
|
8957
9007
|
*/
|
|
8958
|
-
write(phdrData, index) {
|
|
9008
|
+
write(phdrData, index, writeLSB) {
|
|
8959
9009
|
SpessaLog.info(`%cWriting ${this.name}...`, ConsoleColors.info);
|
|
8960
9010
|
writeBinaryStringIndexed(phdrData.pdta, this.name.slice(0, 20), 20);
|
|
8961
9011
|
writeBinaryStringIndexed(phdrData.xdta, this.name.slice(20), 20);
|
|
8962
9012
|
writeWord(phdrData.pdta, this.program);
|
|
8963
9013
|
let wBank = this.bankMSB;
|
|
8964
|
-
if (
|
|
9014
|
+
if (writeLSB) {
|
|
9015
|
+
wBank = this.bankMSB & 127 | (this.bankLSB & 127) << 8;
|
|
9016
|
+
if (this.isGMGSDrum) wBank |= 128;
|
|
9017
|
+
} else if (this.isGMGSDrum) wBank = 128;
|
|
8965
9018
|
else if (this.bankMSB === 0) wBank = this.bankLSB;
|
|
8966
9019
|
writeWord(phdrData.pdta, wBank);
|
|
8967
9020
|
phdrData.xdta.currentIndex += 4;
|
|
@@ -9150,6 +9203,11 @@ var BasicInstrument = class {
|
|
|
9150
9203
|
}
|
|
9151
9204
|
}
|
|
9152
9205
|
}
|
|
9206
|
+
/**
|
|
9207
|
+
* @internal
|
|
9208
|
+
* @param instData
|
|
9209
|
+
* @param index
|
|
9210
|
+
*/
|
|
9153
9211
|
write(instData, index) {
|
|
9154
9212
|
SpessaLog.info(`%cWriting ${this.name}...`, ConsoleColors.info);
|
|
9155
9213
|
writeBinaryStringIndexed(instData.pdta, this.name.slice(0, 20), 20);
|
|
@@ -9160,7 +9218,7 @@ var BasicInstrument = class {
|
|
|
9160
9218
|
};
|
|
9161
9219
|
//#endregion
|
|
9162
9220
|
//#region src/soundbank/soundfont/write/sdta.ts
|
|
9163
|
-
function getSDTA(bank, smplStartOffsets, smplEndOffsets, progressFunction) {
|
|
9221
|
+
function getSDTA(bank, smplStartOffsets, smplEndOffsets, rf64, progressFunction) {
|
|
9164
9222
|
let writtenCount = 0;
|
|
9165
9223
|
const sampleData = [];
|
|
9166
9224
|
const sampleSize = [];
|
|
@@ -9173,8 +9231,8 @@ function getSDTA(bank, smplStartOffsets, smplEndOffsets, progressFunction) {
|
|
|
9173
9231
|
sampleSize.push(r.length);
|
|
9174
9232
|
if (!s.isCompressed) sampleData.push(new Uint8Array(92));
|
|
9175
9233
|
}
|
|
9176
|
-
const smpl = RIFFChunk.getParts("smpl", sampleData);
|
|
9177
|
-
const sdta = RIFFChunk.getParts("sdta", smpl, true);
|
|
9234
|
+
const smpl = RIFFChunk.getParts("smpl", sampleData, rf64);
|
|
9235
|
+
const sdta = RIFFChunk.getParts("sdta", smpl, rf64, true);
|
|
9178
9236
|
let offset = 0;
|
|
9179
9237
|
for (const [i, sample] of bank.samples.entries()) {
|
|
9180
9238
|
const size = sampleSize[i];
|
|
@@ -9314,7 +9372,7 @@ function readSample(index, sampleHeaderData, smplArrayData) {
|
|
|
9314
9372
|
}
|
|
9315
9373
|
//#endregion
|
|
9316
9374
|
//#region src/soundbank/soundfont/write/shdr.ts
|
|
9317
|
-
function getSHDR(bank, smplStartOffsets, smplEndOffsets) {
|
|
9375
|
+
function getSHDR(bank, smplStartOffsets, smplEndOffsets, rf64) {
|
|
9318
9376
|
const sampleLength = 46;
|
|
9319
9377
|
const shdrSize = sampleLength * (bank.samples.length + 1);
|
|
9320
9378
|
const shdrData = new IndexedByteArray(shdrSize);
|
|
@@ -9353,13 +9411,13 @@ function getSHDR(bank, smplStartOffsets, smplEndOffsets) {
|
|
|
9353
9411
|
writeBinaryStringIndexed(shdrData, "EOS", sampleLength);
|
|
9354
9412
|
writeBinaryStringIndexed(xshdrData, "EOS", sampleLength);
|
|
9355
9413
|
return {
|
|
9356
|
-
pdta: RIFFChunk.write("shdr", shdrData),
|
|
9357
|
-
xdta: RIFFChunk.write("shdr", xshdrData)
|
|
9414
|
+
pdta: RIFFChunk.write("shdr", shdrData, rf64),
|
|
9415
|
+
xdta: RIFFChunk.write("shdr", xshdrData, rf64)
|
|
9358
9416
|
};
|
|
9359
9417
|
}
|
|
9360
9418
|
//#endregion
|
|
9361
9419
|
//#region src/soundbank/soundfont/write/write_sf2_elements.ts
|
|
9362
|
-
function writeSF2Elements(bank, isPreset = false) {
|
|
9420
|
+
function writeSF2Elements(bank, rf64, isPreset, writeBankLSB = false) {
|
|
9363
9421
|
const elements = isPreset ? bank.presets : bank.instruments;
|
|
9364
9422
|
const genHeader = isPreset ? "pgen" : "igen";
|
|
9365
9423
|
const modHeader = isPreset ? "pmod" : "imod";
|
|
@@ -9416,7 +9474,7 @@ function writeSF2Elements(bank, isPreset = false) {
|
|
|
9416
9474
|
pdta: new IndexedByteArray(hdrSize),
|
|
9417
9475
|
xdta: new IndexedByteArray(hdrSize)
|
|
9418
9476
|
};
|
|
9419
|
-
for (const [i, el] of elements.entries()) el.write(hdrData, zoneIndexes[i]);
|
|
9477
|
+
for (const [i, el] of elements.entries()) el.write(hdrData, zoneIndexes[i], writeBankLSB);
|
|
9420
9478
|
if (isPreset) {
|
|
9421
9479
|
writeBinaryStringIndexed(hdrData.pdta, "EOP", 20);
|
|
9422
9480
|
hdrData.pdta.currentIndex += 4;
|
|
@@ -9435,20 +9493,20 @@ function writeSF2Elements(bank, isPreset = false) {
|
|
|
9435
9493
|
return {
|
|
9436
9494
|
writeXdta: Math.max(currentGenIndex, currentModIndex, zoneIndex) > 65535,
|
|
9437
9495
|
gen: {
|
|
9438
|
-
pdta: RIFFChunk.write(genHeader, genData),
|
|
9439
|
-
xdta: RIFFChunk.write(modHeader, new IndexedByteArray(4))
|
|
9496
|
+
pdta: RIFFChunk.write(genHeader, genData, rf64),
|
|
9497
|
+
xdta: RIFFChunk.write(modHeader, new IndexedByteArray(4), rf64)
|
|
9440
9498
|
},
|
|
9441
9499
|
mod: {
|
|
9442
|
-
pdta: RIFFChunk.write(modHeader, modData),
|
|
9443
|
-
xdta: RIFFChunk.write(modHeader, new IndexedByteArray(10))
|
|
9500
|
+
pdta: RIFFChunk.write(modHeader, modData, rf64),
|
|
9501
|
+
xdta: RIFFChunk.write(modHeader, new IndexedByteArray(10), rf64)
|
|
9444
9502
|
},
|
|
9445
9503
|
bag: {
|
|
9446
|
-
pdta: RIFFChunk.write(bagHeader, bagData.pdta),
|
|
9447
|
-
xdta: RIFFChunk.write(bagHeader, bagData.xdta)
|
|
9504
|
+
pdta: RIFFChunk.write(bagHeader, bagData.pdta, rf64),
|
|
9505
|
+
xdta: RIFFChunk.write(bagHeader, bagData.xdta, rf64)
|
|
9448
9506
|
},
|
|
9449
9507
|
hdr: {
|
|
9450
|
-
pdta: RIFFChunk.write(hdrHeader, hdrData.pdta),
|
|
9451
|
-
xdta: RIFFChunk.write(hdrHeader, hdrData.xdta)
|
|
9508
|
+
pdta: RIFFChunk.write(hdrHeader, hdrData.pdta, rf64),
|
|
9509
|
+
xdta: RIFFChunk.write(hdrHeader, hdrData.xdta, rf64)
|
|
9452
9510
|
}
|
|
9453
9511
|
};
|
|
9454
9512
|
}
|
|
@@ -9459,6 +9517,10 @@ const DEFAULT_SF2_WRITE_OPTIONS = {
|
|
|
9459
9517
|
writeExtendedLimits: true,
|
|
9460
9518
|
software: "SpessaSynth"
|
|
9461
9519
|
};
|
|
9520
|
+
const DEFAULT_SFE_WRITE_OPTIONS = {
|
|
9521
|
+
rf64: true,
|
|
9522
|
+
software: "SpessaSynth"
|
|
9523
|
+
};
|
|
9462
9524
|
/**
|
|
9463
9525
|
* Writes the sound bank as an SF2 file.
|
|
9464
9526
|
* @param bank
|
|
@@ -9467,6 +9529,28 @@ const DEFAULT_SF2_WRITE_OPTIONS = {
|
|
|
9467
9529
|
*/
|
|
9468
9530
|
function writeSF2Internal(bank, writeOptions) {
|
|
9469
9531
|
const options = fillWithDefaults(writeOptions, DEFAULT_SF2_WRITE_OPTIONS);
|
|
9532
|
+
return writeSF(bank, options.software, options.writeDefaultModulators, options.writeExtendedLimits, false, false);
|
|
9533
|
+
}
|
|
9534
|
+
/**
|
|
9535
|
+
* Writes the sound bank as an SFE 4 file.
|
|
9536
|
+
* @param bank
|
|
9537
|
+
* @param writeOptions the options for writing.
|
|
9538
|
+
* @returns the binary file data.
|
|
9539
|
+
*/
|
|
9540
|
+
function writeSFEInternal(bank, writeOptions) {
|
|
9541
|
+
return writeSF(bank, fillWithDefaults(writeOptions, DEFAULT_SFE_WRITE_OPTIONS).software, true, true, true, true);
|
|
9542
|
+
}
|
|
9543
|
+
/**
|
|
9544
|
+
* General writing function for both SFE and SF2.
|
|
9545
|
+
* @param bank the bank
|
|
9546
|
+
* @param software software param
|
|
9547
|
+
* @param writeDefaultModulators SFE + SF2 compatible
|
|
9548
|
+
* @param writeExtendedLimits SFE + SF2 compatible
|
|
9549
|
+
* @param writeBankLSB SFE Only
|
|
9550
|
+
* @param rf64 SFE Only
|
|
9551
|
+
* @internal
|
|
9552
|
+
*/
|
|
9553
|
+
function writeSF(bank, software, writeDefaultModulators, writeExtendedLimits, writeBankLSB, rf64) {
|
|
9470
9554
|
SpessaLog.groupCollapsed("%cSaving soundbank...", ConsoleColors.info);
|
|
9471
9555
|
SpessaLog.group("%cWriting INFO...", ConsoleColors.info);
|
|
9472
9556
|
/**
|
|
@@ -9475,14 +9559,14 @@ function writeSF2Internal(bank, writeOptions) {
|
|
|
9475
9559
|
const infoArrays = [];
|
|
9476
9560
|
const writeSF2Info = (type, data) => {
|
|
9477
9561
|
if (!data) return;
|
|
9478
|
-
infoArrays.push(...RIFFChunk.getParts(type, [getStringBytes(data, true, true)]));
|
|
9562
|
+
infoArrays.push(...RIFFChunk.getParts(type, [getStringBytes(data, true, true)], rf64));
|
|
9479
9563
|
};
|
|
9480
9564
|
const info = bank.soundBankInfo;
|
|
9481
9565
|
{
|
|
9482
9566
|
const ifilData = new IndexedByteArray(4);
|
|
9483
9567
|
writeWord(ifilData, info.version.major);
|
|
9484
9568
|
writeWord(ifilData, info.version.minor);
|
|
9485
|
-
infoArrays.push(RIFFChunk.write("ifil", ifilData));
|
|
9569
|
+
infoArrays.push(RIFFChunk.write("ifil", ifilData, rf64));
|
|
9486
9570
|
}
|
|
9487
9571
|
writeSF2Info("isng", info.soundEngine);
|
|
9488
9572
|
writeSF2Info("INAM", info.name);
|
|
@@ -9491,36 +9575,35 @@ function writeSF2Internal(bank, writeOptions) {
|
|
|
9491
9575
|
const ifilData = new IndexedByteArray(4);
|
|
9492
9576
|
writeWord(ifilData, info.romVersion.major);
|
|
9493
9577
|
writeWord(ifilData, info.romVersion.minor);
|
|
9494
|
-
infoArrays.push(RIFFChunk.write("iver", ifilData));
|
|
9578
|
+
infoArrays.push(RIFFChunk.write("iver", ifilData, rf64));
|
|
9495
9579
|
}
|
|
9496
9580
|
writeSF2Info("ICRD", toISODateString(info.creationDate));
|
|
9497
9581
|
writeSF2Info("IENG", info.engineer);
|
|
9498
9582
|
writeSF2Info("IPRD", info.product);
|
|
9499
9583
|
writeSF2Info("ICOP", info.copyright);
|
|
9500
9584
|
writeSF2Info("ICMT", info?.subject ? (info?.comment ? info.comment + "\n" : "") + info.subject : info?.comment);
|
|
9501
|
-
const software = options.software;
|
|
9502
9585
|
writeSF2Info("ISFT", software);
|
|
9503
|
-
if (bank.defaultModulators.some((mod) => !SPESSASYNTH_DEFAULT_MODULATORS.some((m) => Modulator.isIdentical(m, mod, true))) &&
|
|
9586
|
+
if (bank.defaultModulators.some((mod) => !SPESSASYNTH_DEFAULT_MODULATORS.some((m) => Modulator.isIdentical(m, mod, true))) && writeDefaultModulators) {
|
|
9504
9587
|
const mods = bank.defaultModulators;
|
|
9505
9588
|
SpessaLog.info(`%cWriting %c${mods.length}%c default modulators...`, ConsoleColors.info, ConsoleColors.recognized, ConsoleColors.info);
|
|
9506
9589
|
const dmodData = new IndexedByteArray(10 + mods.length * 10);
|
|
9507
9590
|
for (const mod of mods) mod.write(dmodData);
|
|
9508
9591
|
writeLittleEndianIndexed(dmodData, 0, 10);
|
|
9509
|
-
infoArrays.push(...RIFFChunk.getParts("DMOD", [dmodData]));
|
|
9592
|
+
infoArrays.push(...RIFFChunk.getParts("DMOD", [dmodData], rf64));
|
|
9510
9593
|
}
|
|
9511
9594
|
SpessaLog.groupEnd();
|
|
9512
9595
|
SpessaLog.info("%cWriting SDTA...", ConsoleColors.info);
|
|
9513
9596
|
const smplStartOffsets = [];
|
|
9514
9597
|
const smplEndOffsets = [];
|
|
9515
|
-
const sdtaChunk = getSDTA(bank, smplStartOffsets, smplEndOffsets);
|
|
9598
|
+
const sdtaChunk = getSDTA(bank, smplStartOffsets, smplEndOffsets, rf64);
|
|
9516
9599
|
SpessaLog.info("%cWriting PDTA...", ConsoleColors.info);
|
|
9517
9600
|
SpessaLog.info("%cWriting SHDR...", ConsoleColors.info);
|
|
9518
|
-
const shdrChunk = getSHDR(bank, smplStartOffsets, smplEndOffsets);
|
|
9601
|
+
const shdrChunk = getSHDR(bank, smplStartOffsets, smplEndOffsets, rf64);
|
|
9519
9602
|
SpessaLog.group("%cWriting instruments...", ConsoleColors.info);
|
|
9520
|
-
const instData = writeSF2Elements(bank, false);
|
|
9603
|
+
const instData = writeSF2Elements(bank, rf64, false);
|
|
9521
9604
|
SpessaLog.groupEnd();
|
|
9522
9605
|
SpessaLog.group("%cWriting presets...", ConsoleColors.info);
|
|
9523
|
-
const presData = writeSF2Elements(bank, true);
|
|
9606
|
+
const presData = writeSF2Elements(bank, rf64, true, writeBankLSB);
|
|
9524
9607
|
SpessaLog.groupEnd();
|
|
9525
9608
|
const chunks = [
|
|
9526
9609
|
presData.hdr,
|
|
@@ -9533,19 +9616,19 @@ function writeSF2Internal(bank, writeOptions) {
|
|
|
9533
9616
|
instData.gen,
|
|
9534
9617
|
shdrChunk
|
|
9535
9618
|
];
|
|
9536
|
-
const pdtaChunk = RIFFChunk.getParts("pdta", chunks.map((c) => c.pdta), true);
|
|
9537
|
-
if (
|
|
9619
|
+
const pdtaChunk = RIFFChunk.getParts("pdta", chunks.map((c) => c.pdta), rf64, true);
|
|
9620
|
+
if (writeExtendedLimits && (instData.writeXdta || presData.writeXdta || bank.presets.some((p) => p.name.length > 20) || bank.instruments.some((i) => i.name.length > 20) || bank.samples.some((s) => s.name.length > 20))) {
|
|
9538
9621
|
SpessaLog.info(`%cWriting the xdta chunk as writeExtendedLimits is enabled and at least one condition was met.`, ConsoleColors.info, ConsoleColors.value);
|
|
9539
|
-
infoArrays.push(...RIFFChunk.getParts("xdta", chunks.map((c) => c.xdta), true));
|
|
9622
|
+
infoArrays.push(...RIFFChunk.getParts("xdta", chunks.map((c) => c.xdta), rf64, true));
|
|
9540
9623
|
}
|
|
9541
|
-
const infoChunk = RIFFChunk.getParts("INFO", infoArrays, true);
|
|
9624
|
+
const infoChunk = RIFFChunk.getParts("INFO", infoArrays, rf64, true);
|
|
9542
9625
|
SpessaLog.info("%cWriting the output file...", ConsoleColors.info);
|
|
9543
|
-
const main = RIFFChunk.writeParts("RIFF", [
|
|
9544
|
-
getStringBytes("sfbk"),
|
|
9626
|
+
const main = RIFFChunk.writeParts(rf64 ? "RIFS" : "RIFF", [
|
|
9627
|
+
getStringBytes(writeBankLSB ? "sfen" : "sfbk"),
|
|
9545
9628
|
...infoChunk,
|
|
9546
9629
|
...sdtaChunk,
|
|
9547
9630
|
...pdtaChunk
|
|
9548
|
-
]);
|
|
9631
|
+
], rf64);
|
|
9549
9632
|
SpessaLog.info(`%cSaved successfully! Final file size: %c${main.length}`, ConsoleColors.info, ConsoleColors.recognized);
|
|
9550
9633
|
SpessaLog.groupEnd();
|
|
9551
9634
|
return main.buffer;
|
|
@@ -9967,7 +10050,7 @@ var DownloadableSoundsSample = class DownloadableSoundsSample extends DLSVerifie
|
|
|
9967
10050
|
wsmp,
|
|
9968
10051
|
...data,
|
|
9969
10052
|
info
|
|
9970
|
-
], true);
|
|
10053
|
+
], false, true);
|
|
9971
10054
|
}
|
|
9972
10055
|
writeFmt() {
|
|
9973
10056
|
const fmtData = new IndexedByteArray(18);
|
|
@@ -10895,7 +10978,7 @@ var DownloadableSoundsRegion = class DownloadableSoundsRegion extends DLSVerifie
|
|
|
10895
10978
|
this.waveLink.write(),
|
|
10896
10979
|
...this.articulation.write()
|
|
10897
10980
|
];
|
|
10898
|
-
return RIFFChunk.getParts("rgn2", chunks, true);
|
|
10981
|
+
return RIFFChunk.getParts("rgn2", chunks, false, true);
|
|
10899
10982
|
}
|
|
10900
10983
|
toSFZone(instrument, samples) {
|
|
10901
10984
|
const sample = samples[this.waveLink.tableIndex];
|
|
@@ -11016,12 +11099,12 @@ var DownloadableSoundsInstrument = class DownloadableSoundsInstrument extends DL
|
|
|
11016
11099
|
SpessaLog.groupCollapsed(`%cWriting %c${this.name}%c...`, ConsoleColors.info, ConsoleColors.recognized, ConsoleColors.info);
|
|
11017
11100
|
const chunks = [this.writeHeader()];
|
|
11018
11101
|
const regionChunks = this.regions.flatMap((r) => r.write());
|
|
11019
|
-
chunks.push(...RIFFChunk.getParts("lrgn", regionChunks, true));
|
|
11102
|
+
chunks.push(...RIFFChunk.getParts("lrgn", regionChunks, false, true));
|
|
11020
11103
|
if (this.articulation.length > 0) chunks.push(...this.articulation.write());
|
|
11021
11104
|
const inam = RIFFChunk.write("INAM", getStringBytes(this.name, true));
|
|
11022
11105
|
chunks.push(RIFFChunk.write("INFO", inam, false, true));
|
|
11023
11106
|
SpessaLog.groupEnd();
|
|
11024
|
-
return RIFFChunk.writeParts("ins ", chunks, true);
|
|
11107
|
+
return RIFFChunk.writeParts("ins ", chunks, false, true);
|
|
11025
11108
|
}
|
|
11026
11109
|
/**
|
|
11027
11110
|
* Performs the full DLS to SF2 instrument conversion.
|
|
@@ -11076,7 +11159,7 @@ var DownloadableSounds = class DownloadableSounds extends DLSVerifier {
|
|
|
11076
11159
|
if (!buffer) throw new Error("No data provided!");
|
|
11077
11160
|
const dataArray = new IndexedByteArray(buffer);
|
|
11078
11161
|
SpessaLog.group("%cParsing DLS file...", ConsoleColors.info);
|
|
11079
|
-
const firstChunk = RIFFChunk.read(dataArray, false);
|
|
11162
|
+
const firstChunk = RIFFChunk.read(dataArray, false, false);
|
|
11080
11163
|
this.verifyHeader(firstChunk, "RIFF");
|
|
11081
11164
|
this.verifyText(readBinaryStringIndexed(dataArray, 4).toLowerCase(), "dls ");
|
|
11082
11165
|
/**
|
|
@@ -11230,7 +11313,7 @@ var DownloadableSounds = class DownloadableSounds extends DLSVerifier {
|
|
|
11230
11313
|
writeDword(colhNum, this.instruments.length);
|
|
11231
11314
|
const colh = RIFFChunk.write("colh", colhNum);
|
|
11232
11315
|
SpessaLog.groupCollapsed("%cWriting instruments...", ConsoleColors.info);
|
|
11233
|
-
const lins = RIFFChunk.getParts("lins", this.instruments.map((i) => i.write()), true);
|
|
11316
|
+
const lins = RIFFChunk.getParts("lins", this.instruments.map((i) => i.write()), false, true);
|
|
11234
11317
|
SpessaLog.info("%cSuccess!", ConsoleColors.recognized);
|
|
11235
11318
|
SpessaLog.groupEnd();
|
|
11236
11319
|
SpessaLog.groupCollapsed("%cWriting WAVE samples...", ConsoleColors.info);
|
|
@@ -11247,7 +11330,7 @@ var DownloadableSounds = class DownloadableSounds extends DLSVerifier {
|
|
|
11247
11330
|
samples.push(...out);
|
|
11248
11331
|
written++;
|
|
11249
11332
|
}
|
|
11250
|
-
const wvpl = RIFFChunk.getParts("wvpl", samples, true);
|
|
11333
|
+
const wvpl = RIFFChunk.getParts("wvpl", samples, false, true);
|
|
11251
11334
|
SpessaLog.info("%cSucceeded!", ConsoleColors.recognized);
|
|
11252
11335
|
const ptblData = new IndexedByteArray(8 + 4 * ptblOffsets.length);
|
|
11253
11336
|
writeDword(ptblData, 8);
|
|
@@ -11276,7 +11359,7 @@ var DownloadableSounds = class DownloadableSounds extends DLSVerifier {
|
|
|
11276
11359
|
...lins,
|
|
11277
11360
|
ptbl,
|
|
11278
11361
|
...wvpl,
|
|
11279
|
-
...RIFFChunk.getParts("INFO", infos, true)
|
|
11362
|
+
...RIFFChunk.getParts("INFO", infos, false, true)
|
|
11280
11363
|
]);
|
|
11281
11364
|
SpessaLog.info("%cSaved successfully!", ConsoleColors.recognized);
|
|
11282
11365
|
SpessaLog.groupEnd();
|
|
@@ -11311,7 +11394,7 @@ var BasicSoundBank = class BasicSoundBank {
|
|
|
11311
11394
|
static isSF3DecoderReady = stb.isInitialized;
|
|
11312
11395
|
/**
|
|
11313
11396
|
* The type of the sound bank that was loaded.
|
|
11314
|
-
* Either `sf2` for SoundFont2/SoundFont3 or `dls` for DownLoadable Sounds.
|
|
11397
|
+
* Either `sf2` for SoundFont2/SoundFont3 or `dls` for DownLoadable Sounds or `sfe` for SF-Enhanced.
|
|
11315
11398
|
*
|
|
11316
11399
|
* Please note that SF3 or SFOGG files are parsed as `sf2` files, but with compressed samples.
|
|
11317
11400
|
* The type is still `sf2`.
|
|
@@ -11390,7 +11473,7 @@ var BasicSoundBank = class BasicSoundBank {
|
|
|
11390
11473
|
sample.name = "Saw";
|
|
11391
11474
|
sample.originalKey = 65;
|
|
11392
11475
|
sample.pitchCorrection = 20;
|
|
11393
|
-
sample.loopEnd =
|
|
11476
|
+
sample.loopEnd = 128;
|
|
11394
11477
|
sample.setAudioData(sampleData, 44100);
|
|
11395
11478
|
font.addSamples(sample);
|
|
11396
11479
|
const inst = new BasicInstrument();
|
|
@@ -11403,7 +11486,7 @@ var BasicSoundBank = class BasicSoundBank {
|
|
|
11403
11486
|
preset.name = "Saw Wave";
|
|
11404
11487
|
preset.createZone(inst);
|
|
11405
11488
|
font.addPresets(preset);
|
|
11406
|
-
font.soundBankInfo.name = "
|
|
11489
|
+
font.soundBankInfo.name = "SpessaSynth Sample Sound Bank";
|
|
11407
11490
|
font.flush();
|
|
11408
11491
|
return font.writeSF2();
|
|
11409
11492
|
}
|
|
@@ -11486,6 +11569,16 @@ var BasicSoundBank = class BasicSoundBank {
|
|
|
11486
11569
|
writeSF2(writeOptions = DEFAULT_SF2_WRITE_OPTIONS) {
|
|
11487
11570
|
return writeSF2Internal(this, writeOptions);
|
|
11488
11571
|
}
|
|
11572
|
+
/**
|
|
11573
|
+
* Writes the sound bank as an [SFE 4](https://sfe-team-was-taken.github.io/SFE/) file.
|
|
11574
|
+
* This enables features such as bank LSB and RIFF64.
|
|
11575
|
+
* Note that spessasynth is currently the only software that can read these files.
|
|
11576
|
+
* @param writeOptions the options for writing.
|
|
11577
|
+
* @returns the binary file data.
|
|
11578
|
+
*/
|
|
11579
|
+
writeSFE(writeOptions = DEFAULT_SFE_WRITE_OPTIONS) {
|
|
11580
|
+
return writeSFEInternal(this, writeOptions);
|
|
11581
|
+
}
|
|
11489
11582
|
addPresets(...presets) {
|
|
11490
11583
|
this.presets.push(...presets);
|
|
11491
11584
|
}
|
|
@@ -11941,33 +12034,29 @@ var SoundFont2 = class extends BasicSoundBank {
|
|
|
11941
12034
|
/**
|
|
11942
12035
|
* Initializes a new SoundFont2 Parser and parses the given data array
|
|
11943
12036
|
*/
|
|
11944
|
-
constructor(arrayBuffer,
|
|
11945
|
-
super("sf2");
|
|
11946
|
-
if (warnDeprecated) throw new Error("Using the constructor directly is deprecated. Use SoundBankLoader.fromArrayBuffer() instead.");
|
|
12037
|
+
constructor(arrayBuffer, sfe) {
|
|
12038
|
+
super(sfe ? "sfe" : "sf2");
|
|
11947
12039
|
const mainFileArray = new IndexedByteArray(arrayBuffer);
|
|
11948
12040
|
SpessaLog.group("%cParsing a SoundFont2 file...", ConsoleColors.info);
|
|
11949
|
-
|
|
11950
|
-
|
|
11951
|
-
|
|
11952
|
-
|
|
11953
|
-
|
|
11954
|
-
this.verifyHeader(firstChunk, "riff");
|
|
12041
|
+
const fourCC = readBinaryString(mainFileArray, 4).toLowerCase();
|
|
12042
|
+
this.verifyTexts(fourCC, ["riff", "rifs"]);
|
|
12043
|
+
const rf64 = fourCC === "rifs";
|
|
12044
|
+
if (rf64) SpessaLog.info("%cRIFF64 Detected!", ConsoleColors.recognized);
|
|
12045
|
+
RIFFChunk.read(mainFileArray, rf64, false);
|
|
11955
12046
|
const type = readBinaryStringIndexed(mainFileArray, 4).toLowerCase();
|
|
11956
|
-
|
|
11957
|
-
|
|
11958
|
-
|
|
11959
|
-
|
|
12047
|
+
this.verifyTexts(type, [
|
|
12048
|
+
"sfbk",
|
|
12049
|
+
"sfpk",
|
|
12050
|
+
"sfen"
|
|
12051
|
+
]);
|
|
11960
12052
|
const isSF2Pack = type === "sfpk";
|
|
11961
|
-
const infoChunk = RIFFChunk.read(mainFileArray);
|
|
12053
|
+
const infoChunk = RIFFChunk.read(mainFileArray, rf64);
|
|
11962
12054
|
this.verifyHeader(infoChunk, "list");
|
|
11963
12055
|
const infoString = readBinaryStringIndexed(infoChunk.data, 4);
|
|
11964
|
-
|
|
11965
|
-
SpessaLog.groupEnd();
|
|
11966
|
-
throw new SyntaxError(`Invalid soundFont! Expected "INFO" got "${infoString}"`);
|
|
11967
|
-
}
|
|
12056
|
+
this.verifyText(infoString, "info");
|
|
11968
12057
|
let xdtaChunk;
|
|
11969
12058
|
while (infoChunk.data.length > infoChunk.data.currentIndex) {
|
|
11970
|
-
const chunk = RIFFChunk.read(infoChunk.data);
|
|
12059
|
+
const chunk = RIFFChunk.read(infoChunk.data, rf64);
|
|
11971
12060
|
const text = readBinaryString(chunk.data, chunk.data.length);
|
|
11972
12061
|
const headerTyped = chunk.header;
|
|
11973
12062
|
switch (headerTyped) {
|
|
@@ -12025,27 +12114,27 @@ var SoundFont2 = class extends BasicSoundBank {
|
|
|
12025
12114
|
this.printInfo();
|
|
12026
12115
|
const xChunks = {};
|
|
12027
12116
|
if (xdtaChunk !== void 0) {
|
|
12028
|
-
xChunks.phdr = RIFFChunk.read(xdtaChunk.data);
|
|
12029
|
-
xChunks.pbag = RIFFChunk.read(xdtaChunk.data);
|
|
12030
|
-
xChunks.pmod = RIFFChunk.read(xdtaChunk.data);
|
|
12031
|
-
xChunks.pgen = RIFFChunk.read(xdtaChunk.data);
|
|
12032
|
-
xChunks.inst = RIFFChunk.read(xdtaChunk.data);
|
|
12033
|
-
xChunks.ibag = RIFFChunk.read(xdtaChunk.data);
|
|
12034
|
-
xChunks.imod = RIFFChunk.read(xdtaChunk.data);
|
|
12035
|
-
xChunks.igen = RIFFChunk.read(xdtaChunk.data);
|
|
12036
|
-
xChunks.shdr = RIFFChunk.read(xdtaChunk.data);
|
|
12037
|
-
}
|
|
12038
|
-
const sdtaChunk = RIFFChunk.read(mainFileArray, false);
|
|
12117
|
+
xChunks.phdr = RIFFChunk.read(xdtaChunk.data, rf64);
|
|
12118
|
+
xChunks.pbag = RIFFChunk.read(xdtaChunk.data, rf64);
|
|
12119
|
+
xChunks.pmod = RIFFChunk.read(xdtaChunk.data, rf64);
|
|
12120
|
+
xChunks.pgen = RIFFChunk.read(xdtaChunk.data, rf64);
|
|
12121
|
+
xChunks.inst = RIFFChunk.read(xdtaChunk.data, rf64);
|
|
12122
|
+
xChunks.ibag = RIFFChunk.read(xdtaChunk.data, rf64);
|
|
12123
|
+
xChunks.imod = RIFFChunk.read(xdtaChunk.data, rf64);
|
|
12124
|
+
xChunks.igen = RIFFChunk.read(xdtaChunk.data, rf64);
|
|
12125
|
+
xChunks.shdr = RIFFChunk.read(xdtaChunk.data, rf64);
|
|
12126
|
+
}
|
|
12127
|
+
const sdtaChunk = RIFFChunk.read(mainFileArray, rf64, false);
|
|
12039
12128
|
this.verifyHeader(sdtaChunk, "list");
|
|
12040
12129
|
this.verifyText(readBinaryStringIndexed(mainFileArray, 4), "sdta");
|
|
12041
12130
|
SpessaLog.info("%cVerifying smpl chunk...", ConsoleColors.warn);
|
|
12042
|
-
const sampleDataChunk = RIFFChunk.read(mainFileArray, false);
|
|
12131
|
+
const sampleDataChunk = RIFFChunk.read(mainFileArray, rf64, false);
|
|
12043
12132
|
this.verifyHeader(sampleDataChunk, "smpl");
|
|
12044
12133
|
let sampleData;
|
|
12045
12134
|
if (isSF2Pack) {
|
|
12046
12135
|
SpessaLog.info("%cSF2Pack detected, attempting to decode the smpl chunk...", ConsoleColors.info);
|
|
12047
12136
|
try {
|
|
12048
|
-
sampleData = stb.decode(mainFileArray.buffer.slice(mainFileArray.currentIndex, mainFileArray.currentIndex + sdtaChunk.size -
|
|
12137
|
+
sampleData = stb.decode(mainFileArray.buffer.slice(mainFileArray.currentIndex, mainFileArray.currentIndex + sdtaChunk.size - 4 - sdtaChunk.headerSize)).data[0];
|
|
12049
12138
|
} catch (error) {
|
|
12050
12139
|
SpessaLog.groupEnd();
|
|
12051
12140
|
throw new Error(`SF2Pack Ogg Vorbis decode error: ${error}`, { cause: error });
|
|
@@ -12055,29 +12144,29 @@ var SoundFont2 = class extends BasicSoundBank {
|
|
|
12055
12144
|
sampleData = mainFileArray;
|
|
12056
12145
|
this.sampleDataStartIndex = mainFileArray.currentIndex;
|
|
12057
12146
|
}
|
|
12058
|
-
SpessaLog.info(`%cSkipping sample chunk, length: %c${sdtaChunk.size -
|
|
12059
|
-
mainFileArray.currentIndex += sdtaChunk.size -
|
|
12147
|
+
SpessaLog.info(`%cSkipping sample chunk, length: %c${sdtaChunk.size - 4 - sdtaChunk.headerSize}`, ConsoleColors.info, ConsoleColors.value);
|
|
12148
|
+
mainFileArray.currentIndex += sdtaChunk.size - 4 - sdtaChunk.headerSize;
|
|
12060
12149
|
SpessaLog.info("%cLoading preset data chunk...", ConsoleColors.warn);
|
|
12061
|
-
const presetChunk = RIFFChunk.read(mainFileArray);
|
|
12150
|
+
const presetChunk = RIFFChunk.read(mainFileArray, rf64);
|
|
12062
12151
|
this.verifyHeader(presetChunk, "list");
|
|
12063
12152
|
readBinaryStringIndexed(presetChunk.data, 4);
|
|
12064
|
-
const phdrChunk = RIFFChunk.read(presetChunk.data);
|
|
12153
|
+
const phdrChunk = RIFFChunk.read(presetChunk.data, rf64);
|
|
12065
12154
|
this.verifyHeader(phdrChunk, "phdr");
|
|
12066
|
-
const pbagChunk = RIFFChunk.read(presetChunk.data);
|
|
12155
|
+
const pbagChunk = RIFFChunk.read(presetChunk.data, rf64);
|
|
12067
12156
|
this.verifyHeader(pbagChunk, "pbag");
|
|
12068
|
-
const pmodChunk = RIFFChunk.read(presetChunk.data);
|
|
12157
|
+
const pmodChunk = RIFFChunk.read(presetChunk.data, rf64);
|
|
12069
12158
|
this.verifyHeader(pmodChunk, "pmod");
|
|
12070
|
-
const pgenChunk = RIFFChunk.read(presetChunk.data);
|
|
12159
|
+
const pgenChunk = RIFFChunk.read(presetChunk.data, rf64);
|
|
12071
12160
|
this.verifyHeader(pgenChunk, "pgen");
|
|
12072
|
-
const instChunk = RIFFChunk.read(presetChunk.data);
|
|
12161
|
+
const instChunk = RIFFChunk.read(presetChunk.data, rf64);
|
|
12073
12162
|
this.verifyHeader(instChunk, "inst");
|
|
12074
|
-
const ibagChunk = RIFFChunk.read(presetChunk.data);
|
|
12163
|
+
const ibagChunk = RIFFChunk.read(presetChunk.data, rf64);
|
|
12075
12164
|
this.verifyHeader(ibagChunk, "ibag");
|
|
12076
|
-
const imodChunk = RIFFChunk.read(presetChunk.data);
|
|
12165
|
+
const imodChunk = RIFFChunk.read(presetChunk.data, rf64);
|
|
12077
12166
|
this.verifyHeader(imodChunk, "imod");
|
|
12078
|
-
const igenChunk = RIFFChunk.read(presetChunk.data);
|
|
12167
|
+
const igenChunk = RIFFChunk.read(presetChunk.data, rf64);
|
|
12079
12168
|
this.verifyHeader(igenChunk, "igen");
|
|
12080
|
-
const shdrChunk = RIFFChunk.read(presetChunk.data);
|
|
12169
|
+
const shdrChunk = RIFFChunk.read(presetChunk.data, rf64);
|
|
12081
12170
|
this.verifyHeader(shdrChunk, "shdr");
|
|
12082
12171
|
SpessaLog.info("%cParsing samples...", ConsoleColors.info);
|
|
12083
12172
|
/**
|
|
@@ -12159,6 +12248,12 @@ var SoundFont2 = class extends BasicSoundBank {
|
|
|
12159
12248
|
this.parsingError(`Invalid FourCC: Expected "${expected.toLowerCase()}" got "${text.toLowerCase()}"\``);
|
|
12160
12249
|
}
|
|
12161
12250
|
}
|
|
12251
|
+
verifyTexts(text, expected) {
|
|
12252
|
+
if (!expected.includes(text.toLowerCase())) {
|
|
12253
|
+
SpessaLog.groupEnd();
|
|
12254
|
+
this.parsingError(`Invalid FourCC: Expected ${expected.map((s) => `"${s}"`).join(", ")} but got "${text.toLowerCase()}"`);
|
|
12255
|
+
}
|
|
12256
|
+
}
|
|
12162
12257
|
};
|
|
12163
12258
|
//#endregion
|
|
12164
12259
|
//#region src/soundbank/sound_bank_loader.ts
|
|
@@ -12169,8 +12264,11 @@ var SoundBankLoader = class {
|
|
|
12169
12264
|
* @returns The loaded sound bank, a BasicSoundBank instance.
|
|
12170
12265
|
*/
|
|
12171
12266
|
static fromArrayBuffer(buffer) {
|
|
12172
|
-
|
|
12173
|
-
|
|
12267
|
+
const riffText = readBinaryStringIndexed(new IndexedByteArray(buffer.slice(0, 4)), 4);
|
|
12268
|
+
if (riffText !== "RIFF" && riffText !== "RIFS") throw new Error(`Expected 'RIFF' or 'RIFS' header, got '${riffText}'`);
|
|
12269
|
+
const id = readBinaryStringIndexed(new IndexedByteArray(riffText === "RIFS" ? buffer.slice(12, 16) : buffer.slice(8, 12)), 4).toLowerCase();
|
|
12270
|
+
if (id === "dls ") return this.loadDLS(buffer);
|
|
12271
|
+
return new SoundFont2(buffer, id === "sfen");
|
|
12174
12272
|
}
|
|
12175
12273
|
static loadDLS(buffer) {
|
|
12176
12274
|
return DownloadableSounds.read(buffer).toSF();
|
|
@@ -13004,7 +13102,7 @@ function noteOn(midiNote, velocity, emit = true) {
|
|
|
13004
13102
|
}
|
|
13005
13103
|
const black = this.synthCore.systemParameters.blackMIDIMode;
|
|
13006
13104
|
if (black && this.synthCore.voiceCount > 200 && velocity < 40 || black && velocity < 10 || this._systemParameters.isMuted || !this.preset) return;
|
|
13007
|
-
let realVelocity = clamp(velocity * (this._midiParameters.velocitySenseDepth / 64) + (this._midiParameters.velocitySenseOffset - 64) * 2, 0, 127);
|
|
13105
|
+
let realVelocity = clamp(Math.floor(velocity * (this._midiParameters.velocitySenseDepth / 64) + (this._midiParameters.velocitySenseOffset - 64) * 2), 0, 127);
|
|
13008
13106
|
let soundBankNote = midiNote + this.currentKeyShift;
|
|
13009
13107
|
if (midiNote > 127 || midiNote < 0) return;
|
|
13010
13108
|
const program = this.preset.program;
|
|
@@ -13240,10 +13338,12 @@ var DynamicModulatorManager = class {
|
|
|
13240
13338
|
const centeredNormalized = centeredValue / 64;
|
|
13241
13339
|
const normalizedNotCentered = data / 127;
|
|
13242
13340
|
switch (addr3 & 15) {
|
|
13243
|
-
case 0:
|
|
13244
|
-
|
|
13245
|
-
|
|
13341
|
+
case 0: {
|
|
13342
|
+
const v = Math.min(24, Math.max(-24, centeredValue));
|
|
13343
|
+
this.setModulator(source, isCC, GeneratorTypes.fineTune, v * 100, bipolar);
|
|
13344
|
+
SpessaLog.coolInfo(`Channel ${this.channel} ${sourceName} pitch control`, v, "semitones");
|
|
13246
13345
|
break;
|
|
13346
|
+
}
|
|
13247
13347
|
case 1:
|
|
13248
13348
|
this.setModulator(source, isCC, GeneratorTypes.initialFilterFc, centeredNormalized * 9600, bipolar);
|
|
13249
13349
|
SpessaLog.coolInfo(`Channel ${this.channel} ${sourceName} filter control`, centeredNormalized * 9600, "cents");
|