spessasynth_core 3.26.28 → 3.26.29
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/package.json +1 -1
- package/src/soundfont/basic_soundfont/basic_instrument.js +16 -1
- package/src/soundfont/basic_soundfont/basic_instrument_zone.js +1 -1
- package/src/soundfont/basic_soundfont/basic_sample.js +39 -17
- package/src/soundfont/basic_soundfont/basic_soundbank.js +3 -3
- package/src/soundfont/basic_soundfont/riff_chunk.js +4 -1
- package/src/soundfont/basic_soundfont/write_dls/wave.js +1 -1
- package/src/soundfont/basic_soundfont/write_sf2/ibag.js +28 -10
- package/src/soundfont/basic_soundfont/write_sf2/igen.js +26 -11
- package/src/soundfont/basic_soundfont/write_sf2/imod.js +28 -14
- package/src/soundfont/basic_soundfont/write_sf2/inst.js +28 -10
- package/src/soundfont/basic_soundfont/write_sf2/pbag.js +26 -11
- package/src/soundfont/basic_soundfont/write_sf2/pgen.js +25 -11
- package/src/soundfont/basic_soundfont/write_sf2/phdr.js +45 -20
- package/src/soundfont/basic_soundfont/write_sf2/pmod.js +28 -14
- package/src/soundfont/basic_soundfont/write_sf2/shdr.js +28 -5
- package/src/soundfont/basic_soundfont/write_sf2/write.js +53 -14
- package/src/soundfont/dls/dls_sample.js +179 -18
- package/src/soundfont/dls/read_samples.js +7 -123
- package/src/soundfont/read_sf2/instrument_zones.js +4 -17
- package/src/soundfont/read_sf2/instruments.js +1 -1
- package/src/soundfont/read_sf2/preset_zones.js +6 -19
- package/src/soundfont/read_sf2/presets.js +0 -1
- package/src/soundfont/read_sf2/samples.js +115 -106
- package/src/soundfont/read_sf2/soundfont.js +198 -56
- package/src/soundfont/read_sf2/zones.js +28 -0
|
@@ -7,38 +7,63 @@ const PHDR_SIZE = 38;
|
|
|
7
7
|
|
|
8
8
|
/**
|
|
9
9
|
* @this {BasicSoundBank}
|
|
10
|
-
* @returns {
|
|
10
|
+
* @returns {ReturnedExtendedSf2Chunks}
|
|
11
11
|
*/
|
|
12
12
|
export function getPHDR()
|
|
13
13
|
{
|
|
14
|
-
const
|
|
15
|
-
const
|
|
14
|
+
const phdrSize = this.presets.length * PHDR_SIZE + PHDR_SIZE;
|
|
15
|
+
const phdrData = new IndexedByteArray(phdrSize);
|
|
16
|
+
// https://github.com/spessasus/soundfont-proposals/blob/main/extended_limits.md
|
|
17
|
+
const xphdrData = new IndexedByteArray(phdrSize);
|
|
16
18
|
// the preset start is adjusted in pbag, this is only for the terminal preset index
|
|
17
19
|
let presetStart = 0;
|
|
18
20
|
for (const preset of this.presets)
|
|
19
21
|
{
|
|
20
|
-
writeStringAsBytes(
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
writeWord(
|
|
22
|
+
writeStringAsBytes(phdrData, preset.presetName.substring(0, 20), 20);
|
|
23
|
+
writeStringAsBytes(xphdrData, preset.presetName.substring(20), 20);
|
|
24
|
+
|
|
25
|
+
writeWord(phdrData, preset.program);
|
|
26
|
+
writeWord(phdrData, preset.bank);
|
|
27
|
+
writeWord(phdrData, presetStart & 0xFFFF);
|
|
28
|
+
|
|
29
|
+
xphdrData.currentIndex += 4;
|
|
30
|
+
writeWord(xphdrData, presetStart >> 16);
|
|
31
|
+
|
|
24
32
|
// 3 unused dword, spec says to keep em so we do
|
|
25
|
-
writeDword(
|
|
26
|
-
writeDword(
|
|
27
|
-
writeDword(
|
|
33
|
+
writeDword(phdrData, preset.library);
|
|
34
|
+
writeDword(phdrData, preset.genre);
|
|
35
|
+
writeDword(phdrData, preset.morphology);
|
|
36
|
+
|
|
37
|
+
xphdrData.currentIndex += 12;
|
|
38
|
+
|
|
28
39
|
presetStart += preset.presetZones.length + 1; // global
|
|
29
40
|
}
|
|
30
41
|
// write EOP
|
|
31
|
-
writeStringAsBytes(
|
|
32
|
-
|
|
33
|
-
writeWord(
|
|
34
|
-
|
|
35
|
-
writeDword(phdrdata, 0); // library
|
|
36
|
-
writeDword(phdrdata, 0); // genre
|
|
37
|
-
writeDword(phdrdata, 0); // morphology
|
|
42
|
+
writeStringAsBytes(phdrData, "EOP", 20);
|
|
43
|
+
phdrData.currentIndex += 4; // program, bank
|
|
44
|
+
writeWord(phdrData, presetStart & 0xFFFF);
|
|
45
|
+
phdrData.currentIndex += 12;// library, genre, morphology
|
|
38
46
|
|
|
39
|
-
|
|
47
|
+
writeStringAsBytes(xphdrData, "EOP", 20);
|
|
48
|
+
xphdrData.currentIndex += 4; // program, bank
|
|
49
|
+
writeWord(xphdrData, presetStart >> 16);
|
|
50
|
+
xphdrData.currentIndex += 12;// library, genre, morphology
|
|
51
|
+
|
|
52
|
+
const phdr = writeRIFFChunk(new RiffChunk(
|
|
40
53
|
"phdr",
|
|
41
|
-
|
|
42
|
-
|
|
54
|
+
phdrData.length,
|
|
55
|
+
phdrData
|
|
43
56
|
));
|
|
57
|
+
|
|
58
|
+
const xphdr = writeRIFFChunk(new RiffChunk(
|
|
59
|
+
"phdr",
|
|
60
|
+
xphdrData.length,
|
|
61
|
+
xphdrData
|
|
62
|
+
));
|
|
63
|
+
|
|
64
|
+
return {
|
|
65
|
+
pdta: phdr,
|
|
66
|
+
xdta: xphdr,
|
|
67
|
+
highestIndex: presetStart
|
|
68
|
+
};
|
|
44
69
|
}
|
|
@@ -5,19 +5,19 @@ import { MOD_BYTE_SIZE } from "../modulator.js";
|
|
|
5
5
|
|
|
6
6
|
/**
|
|
7
7
|
* @this {BasicSoundBank}
|
|
8
|
-
* @returns {
|
|
8
|
+
* @returns {ReturnedExtendedSf2Chunks}
|
|
9
9
|
*/
|
|
10
10
|
export function getPMOD()
|
|
11
11
|
{
|
|
12
12
|
// very similar to imod,
|
|
13
13
|
// go through all presets -> zones and write modulators sequentially
|
|
14
|
-
let
|
|
14
|
+
let pmodSize = MOD_BYTE_SIZE;
|
|
15
15
|
for (const preset of this.presets)
|
|
16
16
|
{
|
|
17
|
-
|
|
18
|
-
|
|
17
|
+
pmodSize += preset.globalZone.modulators.length * MOD_BYTE_SIZE;
|
|
18
|
+
pmodSize += preset.presetZones.reduce((sum, z) => z.modulators.length * MOD_BYTE_SIZE + sum, 0);
|
|
19
19
|
}
|
|
20
|
-
const
|
|
20
|
+
const pmodData = new IndexedByteArray(pmodSize);
|
|
21
21
|
|
|
22
22
|
/**
|
|
23
23
|
* @param z {BasicZone}
|
|
@@ -26,11 +26,11 @@ export function getPMOD()
|
|
|
26
26
|
{
|
|
27
27
|
for (const mod of z.modulators)
|
|
28
28
|
{
|
|
29
|
-
writeWord(
|
|
30
|
-
writeWord(
|
|
31
|
-
writeWord(
|
|
32
|
-
writeWord(
|
|
33
|
-
writeWord(
|
|
29
|
+
writeWord(pmodData, mod.getSourceEnum());
|
|
30
|
+
writeWord(pmodData, mod.modulatorDestination);
|
|
31
|
+
writeWord(pmodData, mod.transformAmount);
|
|
32
|
+
writeWord(pmodData, mod.getSecSrcEnum());
|
|
33
|
+
writeWord(pmodData, mod.transformType);
|
|
34
34
|
}
|
|
35
35
|
};
|
|
36
36
|
|
|
@@ -46,11 +46,25 @@ export function getPMOD()
|
|
|
46
46
|
}
|
|
47
47
|
|
|
48
48
|
// terminal modulator, is zero
|
|
49
|
-
writeLittleEndian(
|
|
49
|
+
writeLittleEndian(pmodData, 0, MOD_BYTE_SIZE);
|
|
50
50
|
|
|
51
|
-
|
|
51
|
+
// https://github.com/spessasus/soundfont-proposals/blob/main/extended_limits.md
|
|
52
|
+
const xpmodData = new IndexedByteArray(MOD_BYTE_SIZE);
|
|
53
|
+
writeLittleEndian(xpmodData, 0, MOD_BYTE_SIZE);
|
|
54
|
+
|
|
55
|
+
const pmod = writeRIFFChunk(new RiffChunk(
|
|
56
|
+
"pmod",
|
|
57
|
+
pmodData.length,
|
|
58
|
+
pmodData
|
|
59
|
+
));
|
|
60
|
+
const xpmod = writeRIFFChunk(new RiffChunk(
|
|
52
61
|
"pmod",
|
|
53
|
-
|
|
54
|
-
|
|
62
|
+
xpmodData.length,
|
|
63
|
+
xpmodData
|
|
55
64
|
));
|
|
65
|
+
return {
|
|
66
|
+
pdta: pmod,
|
|
67
|
+
xdta: xpmod,
|
|
68
|
+
highestIndex: 0 // not applicable
|
|
69
|
+
};
|
|
56
70
|
}
|
|
@@ -8,22 +8,29 @@ import { SF3_BIT_FLIT } from "../../read_sf2/samples.js";
|
|
|
8
8
|
* @this {BasicSoundBank}
|
|
9
9
|
* @param smplStartOffsets {number[]}
|
|
10
10
|
* @param smplEndOffsets {number[]}
|
|
11
|
-
* @returns {
|
|
11
|
+
* @returns {ReturnedExtendedSf2Chunks}
|
|
12
12
|
*/
|
|
13
13
|
export function getSHDR(smplStartOffsets, smplEndOffsets)
|
|
14
14
|
{
|
|
15
15
|
const sampleLength = 46;
|
|
16
|
-
const
|
|
16
|
+
const shdrSize = sampleLength * (this.samples.length + 1); // +1 because EOP
|
|
17
|
+
const shdrData = new IndexedByteArray(shdrSize);
|
|
18
|
+
// https://github.com/spessasus/soundfont-proposals/blob/main/extended_limits.md
|
|
19
|
+
const xshdrData = new IndexedByteArray(shdrSize);
|
|
20
|
+
let maxSampleLink = 0;
|
|
17
21
|
this.samples.forEach((sample, index) =>
|
|
18
22
|
{
|
|
19
23
|
// sample name
|
|
20
|
-
writeStringAsBytes(shdrData, sample.sampleName, 20);
|
|
24
|
+
writeStringAsBytes(shdrData, sample.sampleName.substring(0, 20), 20);
|
|
25
|
+
writeStringAsBytes(xshdrData, sample.sampleName.substring(20), 20);
|
|
21
26
|
// start offset
|
|
22
27
|
const dwStart = smplStartOffsets[index];
|
|
23
28
|
writeDword(shdrData, dwStart);
|
|
29
|
+
xshdrData.currentIndex += 4;
|
|
24
30
|
// end offset
|
|
25
31
|
const dwEnd = smplEndOffsets[index];
|
|
26
32
|
writeDword(shdrData, dwEnd);
|
|
33
|
+
xshdrData.currentIndex += 4;
|
|
27
34
|
// loop is stored as relative in sample points, change it to absolute sample points here
|
|
28
35
|
let loopStart = sample.sampleLoopStartIndex + dwStart;
|
|
29
36
|
let loopEnd = sample.sampleLoopEndIndex + dwStart;
|
|
@@ -40,9 +47,13 @@ export function getSHDR(smplStartOffsets, smplEndOffsets)
|
|
|
40
47
|
// pitch and correction
|
|
41
48
|
shdrData[shdrData.currentIndex++] = sample.samplePitch;
|
|
42
49
|
shdrData[shdrData.currentIndex++] = sample.samplePitchCorrection;
|
|
50
|
+
// skip all those for xshdr
|
|
51
|
+
xshdrData.currentIndex += 14;
|
|
43
52
|
// sample link
|
|
44
53
|
const sampleLinkIndex = this.samples.indexOf(sample.linkedSample);
|
|
45
|
-
writeWord(shdrData, Math.max(0, sampleLinkIndex));
|
|
54
|
+
writeWord(shdrData, Math.max(0, sampleLinkIndex) & 0xFFFF);
|
|
55
|
+
writeWord(xshdrData, Math.max(0, sampleLinkIndex) >> 16);
|
|
56
|
+
maxSampleLink = Math.max(maxSampleLink, sampleLinkIndex);
|
|
46
57
|
// sample type: add byte if compressed
|
|
47
58
|
let type = sample.sampleType;
|
|
48
59
|
if (sample.isCompressed)
|
|
@@ -50,13 +61,25 @@ export function getSHDR(smplStartOffsets, smplEndOffsets)
|
|
|
50
61
|
type |= SF3_BIT_FLIT;
|
|
51
62
|
}
|
|
52
63
|
writeWord(shdrData, type);
|
|
64
|
+
xshdrData.currentIndex += 2;
|
|
53
65
|
});
|
|
54
66
|
|
|
55
67
|
// write EOS and zero everything else
|
|
56
68
|
writeStringAsBytes(shdrData, "EOS", sampleLength);
|
|
57
|
-
|
|
69
|
+
writeStringAsBytes(xshdrData, "EOS", sampleLength);
|
|
70
|
+
const shdr = writeRIFFChunk(new RiffChunk(
|
|
58
71
|
"shdr",
|
|
59
72
|
shdrData.length,
|
|
60
73
|
shdrData
|
|
61
74
|
));
|
|
75
|
+
const xshdr = writeRIFFChunk(new RiffChunk(
|
|
76
|
+
"shdr",
|
|
77
|
+
xshdrData.length,
|
|
78
|
+
xshdrData
|
|
79
|
+
));
|
|
80
|
+
return {
|
|
81
|
+
pdta: shdr,
|
|
82
|
+
xdta: xshdr,
|
|
83
|
+
highestIndex: maxSampleLink
|
|
84
|
+
};
|
|
62
85
|
}
|
|
@@ -24,6 +24,16 @@ import { fillWithDefaults } from "../../../utils/fill_with_defaults.js";
|
|
|
24
24
|
* the encode vorbis function. Can be undefined if not compressed.
|
|
25
25
|
* @property {boolean|undefined} writeDefaultModulators - if the DMOD chunk should be written.
|
|
26
26
|
* Recommended.
|
|
27
|
+
* @property {boolean|undefined} writeExtendedLimits - if the xdta chunk should be written to allow virtually infinite parameters.
|
|
28
|
+
* Recommended.
|
|
29
|
+
*/
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* @typedef {Object} ReturnedExtendedSf2Chunks
|
|
34
|
+
* @property {IndexedByteArray} pdta - the pdta part of the chunk
|
|
35
|
+
* @property {IndexedByteArray} xdta - the xdta (https://github.com/spessasus/soundfont-proposals/blob/main/extended_limits.md) part of the chunk
|
|
36
|
+
* @property {number} highestIndex - the highest index written (0 if not applicable). Used for determining whether the xdta chunk is necessary.
|
|
27
37
|
*/
|
|
28
38
|
|
|
29
39
|
/**
|
|
@@ -33,7 +43,8 @@ const DEFAULT_WRITE_OPTIONS = {
|
|
|
33
43
|
compress: false,
|
|
34
44
|
compressionQuality: 0.5,
|
|
35
45
|
compressionFunction: undefined,
|
|
36
|
-
writeDefaultModulators: true
|
|
46
|
+
writeDefaultModulators: true,
|
|
47
|
+
writeExtendedLimits: true
|
|
37
48
|
};
|
|
38
49
|
|
|
39
50
|
/**
|
|
@@ -146,7 +157,6 @@ export function write(options = DEFAULT_WRITE_OPTIONS)
|
|
|
146
157
|
)));
|
|
147
158
|
}
|
|
148
159
|
}
|
|
149
|
-
const infoChunk = writeRIFFOddSize("INFO", combineArrays(infoArrays), false, true);
|
|
150
160
|
|
|
151
161
|
SpessaSynthInfo(
|
|
152
162
|
"%cWriting SDTA...",
|
|
@@ -213,24 +223,53 @@ export function write(options = DEFAULT_WRITE_OPTIONS)
|
|
|
213
223
|
consoleColors.info
|
|
214
224
|
);
|
|
215
225
|
const phdrChunk = getPHDR.call(this);
|
|
226
|
+
/**
|
|
227
|
+
* @type {ReturnedExtendedSf2Chunks[]}
|
|
228
|
+
*/
|
|
229
|
+
const chunks = [phdrChunk, pbagChunk, pmodChunk, pgenChunk, instChunk, ibagChunk, imodChunk, igenChunk, shdrChunk];
|
|
216
230
|
// combine in the sfspec order
|
|
217
|
-
const
|
|
231
|
+
const pdtaData = combineArrays([
|
|
218
232
|
new IndexedByteArray([112, 100, 116, 97]), // "pdta"
|
|
219
|
-
|
|
220
|
-
pbagChunk,
|
|
221
|
-
pmodChunk,
|
|
222
|
-
pgenChunk,
|
|
223
|
-
instChunk,
|
|
224
|
-
ibagChunk,
|
|
225
|
-
imodChunk,
|
|
226
|
-
igenChunk,
|
|
227
|
-
shdrChunk
|
|
233
|
+
...chunks.map(c => c.pdta)
|
|
228
234
|
]);
|
|
229
235
|
const pdtaChunk = writeRIFFChunk(new RiffChunk(
|
|
230
236
|
"LIST",
|
|
231
|
-
|
|
232
|
-
|
|
237
|
+
pdtaData.length,
|
|
238
|
+
pdtaData
|
|
233
239
|
));
|
|
240
|
+
const maxIndex = Math.max(
|
|
241
|
+
...chunks.map(c => c.highestIndex)
|
|
242
|
+
);
|
|
243
|
+
|
|
244
|
+
const writeXdta = options.writeExtendedLimits && (
|
|
245
|
+
maxIndex > 0xFFF
|
|
246
|
+
|| this.presets.some(p => p.presetName.length > 20)
|
|
247
|
+
|| this.instruments.some(i => i.instrumentName.length > 20)
|
|
248
|
+
|| this.samples.some(s => s.sampleName.length > 20)
|
|
249
|
+
);
|
|
250
|
+
|
|
251
|
+
if (writeXdta)
|
|
252
|
+
{
|
|
253
|
+
SpessaSynthInfo(
|
|
254
|
+
`%cWriting the xdta chunk! Max index: %c${maxIndex}`,
|
|
255
|
+
consoleColors.info,
|
|
256
|
+
consoleColors.value
|
|
257
|
+
);
|
|
258
|
+
// https://github.com/spessasus/soundfont-proposals/blob/main/extended_limits.md
|
|
259
|
+
const xpdtaData = combineArrays([
|
|
260
|
+
new IndexedByteArray([120, 100, 116, 97]), // "xdta"
|
|
261
|
+
...chunks.map(c => c.xdta)
|
|
262
|
+
]);
|
|
263
|
+
|
|
264
|
+
const xpdtaChunk = writeRIFFChunk(new RiffChunk(
|
|
265
|
+
"LIST",
|
|
266
|
+
xpdtaData.length,
|
|
267
|
+
xpdtaData
|
|
268
|
+
));
|
|
269
|
+
infoArrays.push(xpdtaChunk);
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
const infoChunk = writeRIFFOddSize("INFO", combineArrays(infoArrays), false, true);
|
|
234
273
|
SpessaSynthInfo(
|
|
235
274
|
"%cWriting the output file...",
|
|
236
275
|
consoleColors.info
|
|
@@ -1,4 +1,116 @@
|
|
|
1
1
|
import { BasicSample, sampleTypes } from "../basic_soundfont/basic_sample.js";
|
|
2
|
+
import { SpessaSynthWarn } from "../../utils/loggin.js";
|
|
3
|
+
import { readLittleEndian } from "../../utils/byte_functions/little_endian.js";
|
|
4
|
+
import { IndexedByteArray } from "../../utils/indexed_array.js";
|
|
5
|
+
|
|
6
|
+
const W_FORMAT_TAG = {
|
|
7
|
+
PCM: 0x01,
|
|
8
|
+
ALAW: 0x6
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* @param data {IndexedByteArray}
|
|
14
|
+
* @param bytesPerSample {number}
|
|
15
|
+
* @returns {Float32Array}
|
|
16
|
+
*/
|
|
17
|
+
function readPCM(data, bytesPerSample)
|
|
18
|
+
{
|
|
19
|
+
const maxSampleValue = Math.pow(2, bytesPerSample * 8 - 1); // Max value for the sample
|
|
20
|
+
const maxUnsigned = Math.pow(2, bytesPerSample * 8);
|
|
21
|
+
|
|
22
|
+
let normalizationFactor;
|
|
23
|
+
let isUnsigned = false;
|
|
24
|
+
|
|
25
|
+
if (bytesPerSample === 1)
|
|
26
|
+
{
|
|
27
|
+
normalizationFactor = 255; // For 8-bit normalize from 0-255
|
|
28
|
+
isUnsigned = true;
|
|
29
|
+
}
|
|
30
|
+
else
|
|
31
|
+
{
|
|
32
|
+
normalizationFactor = maxSampleValue; // For 16-bit normalize from -32,768 to 32,767
|
|
33
|
+
}
|
|
34
|
+
const sampleLength = data.length / bytesPerSample;
|
|
35
|
+
const sampleData = new Float32Array(sampleLength);
|
|
36
|
+
if (bytesPerSample === 2)
|
|
37
|
+
{
|
|
38
|
+
// special optimized case for s16 (most common)
|
|
39
|
+
const s16 = new Int16Array(data.buffer);
|
|
40
|
+
for (let i = 0; i < s16.length; i++)
|
|
41
|
+
{
|
|
42
|
+
sampleData[i] = s16[i] / 32768;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
else
|
|
46
|
+
{
|
|
47
|
+
for (let i = 0; i < sampleData.length; i++)
|
|
48
|
+
{
|
|
49
|
+
// read
|
|
50
|
+
let sample = readLittleEndian(data, bytesPerSample);
|
|
51
|
+
// turn into signed
|
|
52
|
+
if (isUnsigned)
|
|
53
|
+
{
|
|
54
|
+
// normalize unsigned 8-bit sample
|
|
55
|
+
sampleData[i] = (sample / normalizationFactor) - 0.5;
|
|
56
|
+
}
|
|
57
|
+
else
|
|
58
|
+
{
|
|
59
|
+
// normalize signed sample
|
|
60
|
+
if (sample >= maxSampleValue)
|
|
61
|
+
{
|
|
62
|
+
sample -= maxUnsigned;
|
|
63
|
+
}
|
|
64
|
+
sampleData[i] = sample / normalizationFactor;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
return sampleData;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* @param data {IndexedByteArray}
|
|
73
|
+
* @param bytesPerSample {number}
|
|
74
|
+
* @returns {Float32Array}
|
|
75
|
+
*/
|
|
76
|
+
function readALAW(data, bytesPerSample)
|
|
77
|
+
{
|
|
78
|
+
const sampleLength = data / bytesPerSample;
|
|
79
|
+
const sampleData = new Float32Array(sampleLength);
|
|
80
|
+
for (let i = 0; i < sampleData.length; i++)
|
|
81
|
+
{
|
|
82
|
+
// read
|
|
83
|
+
const input = readLittleEndian(data, bytesPerSample);
|
|
84
|
+
|
|
85
|
+
// https://en.wikipedia.org/wiki/G.711#A-law
|
|
86
|
+
// re-toggle toggled bits
|
|
87
|
+
let sample = input ^ 0x55;
|
|
88
|
+
|
|
89
|
+
// remove sign bit
|
|
90
|
+
sample &= 0x7F;
|
|
91
|
+
|
|
92
|
+
// extract exponent
|
|
93
|
+
let exponent = sample >> 4;
|
|
94
|
+
// extract mantissa
|
|
95
|
+
let mantissa = sample & 0xF;
|
|
96
|
+
if (exponent > 0)
|
|
97
|
+
{
|
|
98
|
+
mantissa += 16; // add leading '1', if exponent > 0
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
mantissa = (mantissa << 4) + 0x8;
|
|
102
|
+
if (exponent > 1)
|
|
103
|
+
{
|
|
104
|
+
mantissa = mantissa << (exponent - 1);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
const s16sample = input > 127 ? mantissa : -mantissa;
|
|
108
|
+
|
|
109
|
+
// convert to float
|
|
110
|
+
sampleData[i] = s16sample / 32678;
|
|
111
|
+
}
|
|
112
|
+
return sampleData;
|
|
113
|
+
}
|
|
2
114
|
|
|
3
115
|
export class DLSSample extends BasicSample
|
|
4
116
|
{
|
|
@@ -12,6 +124,22 @@ export class DLSSample extends BasicSample
|
|
|
12
124
|
*/
|
|
13
125
|
sampleData;
|
|
14
126
|
|
|
127
|
+
/**
|
|
128
|
+
* @type {number}
|
|
129
|
+
*/
|
|
130
|
+
wFormatTag;
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* @type {number}
|
|
134
|
+
*/
|
|
135
|
+
bytesPerSample;
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* Sample's raw data before decoding it, for faster writing
|
|
139
|
+
* @type {IndexedByteArray}
|
|
140
|
+
*/
|
|
141
|
+
rawData;
|
|
142
|
+
|
|
15
143
|
/**
|
|
16
144
|
* @param name {string}
|
|
17
145
|
* @param rate {number}
|
|
@@ -19,8 +147,10 @@ export class DLSSample extends BasicSample
|
|
|
19
147
|
* @param pitchCorrection {number}
|
|
20
148
|
* @param loopStart {number} sample data points
|
|
21
149
|
* @param loopEnd {number} sample data points
|
|
22
|
-
* @param data {Float32Array}
|
|
23
150
|
* @param sampleDbAttenuation {number} in db
|
|
151
|
+
* @param dataChunk {RiffChunk}
|
|
152
|
+
* @param wFormatTag {number}
|
|
153
|
+
* @param bytesPerSample {number}
|
|
24
154
|
*/
|
|
25
155
|
constructor(
|
|
26
156
|
name,
|
|
@@ -29,8 +159,10 @@ export class DLSSample extends BasicSample
|
|
|
29
159
|
pitchCorrection,
|
|
30
160
|
loopStart,
|
|
31
161
|
loopEnd,
|
|
32
|
-
|
|
33
|
-
|
|
162
|
+
sampleDbAttenuation,
|
|
163
|
+
dataChunk,
|
|
164
|
+
wFormatTag,
|
|
165
|
+
bytesPerSample
|
|
34
166
|
)
|
|
35
167
|
{
|
|
36
168
|
super(
|
|
@@ -42,12 +174,42 @@ export class DLSSample extends BasicSample
|
|
|
42
174
|
loopStart,
|
|
43
175
|
loopEnd
|
|
44
176
|
);
|
|
45
|
-
this.setAudioData(data);
|
|
46
177
|
this.sampleDbAttenuation = sampleDbAttenuation;
|
|
178
|
+
/**
|
|
179
|
+
* @type {IndexedByteArray}
|
|
180
|
+
*/
|
|
181
|
+
this.rawData = dataChunk.chunkData;
|
|
182
|
+
this.wFormatTag = wFormatTag;
|
|
183
|
+
this.bytesPerSample = bytesPerSample;
|
|
47
184
|
}
|
|
48
185
|
|
|
49
186
|
getAudioData()
|
|
50
187
|
{
|
|
188
|
+
if (!(this.rawData instanceof Uint8Array))
|
|
189
|
+
{
|
|
190
|
+
return new Float32Array(0);
|
|
191
|
+
}
|
|
192
|
+
if (!this.sampleData)
|
|
193
|
+
{
|
|
194
|
+
let sampleData;
|
|
195
|
+
switch (this.wFormatTag)
|
|
196
|
+
{
|
|
197
|
+
default:
|
|
198
|
+
SpessaSynthWarn(`Failed to decode sample. Unknown wFormatTag: ${this.wFormatTag}`);
|
|
199
|
+
sampleData = new Float32Array(this.rawData.length / this.bytesPerSample);
|
|
200
|
+
break;
|
|
201
|
+
|
|
202
|
+
case W_FORMAT_TAG.PCM:
|
|
203
|
+
sampleData = readPCM(this.rawData, this.bytesPerSample);
|
|
204
|
+
break;
|
|
205
|
+
|
|
206
|
+
case W_FORMAT_TAG.ALAW:
|
|
207
|
+
sampleData = readALAW(this.rawData, this.bytesPerSample);
|
|
208
|
+
break;
|
|
209
|
+
|
|
210
|
+
}
|
|
211
|
+
this.setAudioData(sampleData);
|
|
212
|
+
}
|
|
51
213
|
return this.sampleData;
|
|
52
214
|
}
|
|
53
215
|
|
|
@@ -59,24 +221,23 @@ export class DLSSample extends BasicSample
|
|
|
59
221
|
super.setAudioData(audioData);
|
|
60
222
|
}
|
|
61
223
|
|
|
62
|
-
getRawData()
|
|
224
|
+
getRawData(allowVorbis = true)
|
|
63
225
|
{
|
|
64
|
-
if (this.
|
|
226
|
+
if (this.dataOverriden)
|
|
227
|
+
{
|
|
228
|
+
return this.encodeS16LE();
|
|
229
|
+
}
|
|
230
|
+
else
|
|
65
231
|
{
|
|
66
|
-
if (
|
|
232
|
+
if (this.compressedData && allowVorbis)
|
|
233
|
+
{
|
|
234
|
+
return this.compressedData;
|
|
235
|
+
}
|
|
236
|
+
if (this.wFormatTag === W_FORMAT_TAG.PCM && this.bytesPerSample === 2)
|
|
67
237
|
{
|
|
68
|
-
|
|
238
|
+
return this.rawData;
|
|
69
239
|
}
|
|
70
|
-
return this.
|
|
240
|
+
return this.encodeS16LE();
|
|
71
241
|
}
|
|
72
|
-
return super.getRawData();
|
|
73
|
-
// const uint8 = new Uint8Array(this.sampleData.length * 2);
|
|
74
|
-
// for (let i = 0; i < this.sampleData.length; i++)
|
|
75
|
-
// {
|
|
76
|
-
// const sample = Math.floor(this.sampleData[i] * 32768);
|
|
77
|
-
// uint8[i * 2] = sample & 0xFF; // lower byte
|
|
78
|
-
// uint8[i * 2 + 1] = (sample >> 8) & 0xFF; // upper byte
|
|
79
|
-
// }
|
|
80
|
-
// return uint8;
|
|
81
242
|
}
|
|
82
243
|
}
|