spessasynth_core 3.26.27 → 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.
Files changed (26) hide show
  1. package/package.json +1 -1
  2. package/src/soundfont/basic_soundfont/basic_instrument.js +21 -2
  3. package/src/soundfont/basic_soundfont/basic_instrument_zone.js +1 -1
  4. package/src/soundfont/basic_soundfont/basic_sample.js +48 -17
  5. package/src/soundfont/basic_soundfont/basic_soundbank.js +18 -15
  6. package/src/soundfont/basic_soundfont/riff_chunk.js +4 -1
  7. package/src/soundfont/basic_soundfont/write_dls/wave.js +1 -1
  8. package/src/soundfont/basic_soundfont/write_sf2/ibag.js +28 -10
  9. package/src/soundfont/basic_soundfont/write_sf2/igen.js +26 -11
  10. package/src/soundfont/basic_soundfont/write_sf2/imod.js +28 -14
  11. package/src/soundfont/basic_soundfont/write_sf2/inst.js +28 -10
  12. package/src/soundfont/basic_soundfont/write_sf2/pbag.js +26 -11
  13. package/src/soundfont/basic_soundfont/write_sf2/pgen.js +25 -11
  14. package/src/soundfont/basic_soundfont/write_sf2/phdr.js +45 -20
  15. package/src/soundfont/basic_soundfont/write_sf2/pmod.js +28 -14
  16. package/src/soundfont/basic_soundfont/write_sf2/shdr.js +28 -5
  17. package/src/soundfont/basic_soundfont/write_sf2/write.js +53 -14
  18. package/src/soundfont/dls/dls_sample.js +179 -18
  19. package/src/soundfont/dls/read_samples.js +7 -123
  20. package/src/soundfont/read_sf2/instrument_zones.js +4 -17
  21. package/src/soundfont/read_sf2/instruments.js +1 -1
  22. package/src/soundfont/read_sf2/preset_zones.js +6 -19
  23. package/src/soundfont/read_sf2/presets.js +0 -1
  24. package/src/soundfont/read_sf2/samples.js +115 -106
  25. package/src/soundfont/read_sf2/soundfont.js +198 -56
  26. package/src/soundfont/read_sf2/zones.js +28 -0
@@ -7,17 +7,17 @@ import { generatorTypes } from "../generator_types.js";
7
7
 
8
8
  /**
9
9
  * @this {BasicSoundBank}
10
- * @returns {IndexedByteArray}
10
+ * @returns {ReturnedExtendedSf2Chunks}
11
11
  */
12
12
  export function getPGEN()
13
13
  {
14
14
  // almost identical to igen, except the correct instrument instead of sample gen
15
15
  // goes through all preset zones and writes generators sequentially (add 4 for terminal)
16
- let pgensize = GEN_BYTE_SIZE;
16
+ let pgenSize = GEN_BYTE_SIZE;
17
17
  for (const preset of this.presets)
18
18
  {
19
- pgensize += preset.globalZone.generators.length * GEN_BYTE_SIZE;
20
- pgensize += preset.presetZones.reduce((size, z) =>
19
+ pgenSize += preset.globalZone.generators.length * GEN_BYTE_SIZE;
20
+ pgenSize += preset.presetZones.reduce((size, z) =>
21
21
  {
22
22
  // clear instrument and range generators before determining the size
23
23
  z.generators = z.generators.filter(g =>
@@ -51,7 +51,7 @@ export function getPGEN()
51
51
  return z.generators.length * GEN_BYTE_SIZE + size;
52
52
  }, 0);
53
53
  }
54
- const pgendata = new IndexedByteArray(pgensize);
54
+ const pgenData = new IndexedByteArray(pgenSize);
55
55
 
56
56
  /**
57
57
  * @param z {BasicZone}
@@ -61,8 +61,8 @@ export function getPGEN()
61
61
  for (const gen of z.generators)
62
62
  {
63
63
  // name is deceptive, it works on negatives
64
- writeWord(pgendata, gen.generatorType);
65
- writeWord(pgendata, gen.generatorValue);
64
+ writeWord(pgenData, gen.generatorType);
65
+ writeWord(pgenData, gen.generatorValue);
66
66
  }
67
67
  };
68
68
  for (const preset of this.presets)
@@ -75,11 +75,25 @@ export function getPGEN()
75
75
  }
76
76
  }
77
77
  // terminal generator, is zero
78
- writeDword(pgendata, 0);
78
+ writeDword(pgenData, 0);
79
79
 
80
- return writeRIFFChunk(new RiffChunk(
80
+ // https://github.com/spessasus/soundfont-proposals/blob/main/extended_limits.md
81
+ const xpgenData = new IndexedByteArray(GEN_BYTE_SIZE);
82
+ writeDword(xpgenData, 0);
83
+
84
+ const pgen = writeRIFFChunk(new RiffChunk(
85
+ "pgen",
86
+ pgenData.length,
87
+ pgenData
88
+ ));
89
+ const xpgen = writeRIFFChunk(new RiffChunk(
81
90
  "pgen",
82
- pgendata.length,
83
- pgendata
91
+ xpgenData.length,
92
+ xpgenData
84
93
  ));
94
+ return {
95
+ pdta: pgen,
96
+ xdta: xpgen,
97
+ highestIndex: 0 // not applicable
98
+ };
85
99
  }
@@ -7,38 +7,63 @@ const PHDR_SIZE = 38;
7
7
 
8
8
  /**
9
9
  * @this {BasicSoundBank}
10
- * @returns {IndexedByteArray}
10
+ * @returns {ReturnedExtendedSf2Chunks}
11
11
  */
12
12
  export function getPHDR()
13
13
  {
14
- const phdrsize = this.presets.length * PHDR_SIZE + PHDR_SIZE;
15
- const phdrdata = new IndexedByteArray(phdrsize);
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(phdrdata, preset.presetName, 20);
21
- writeWord(phdrdata, preset.program);
22
- writeWord(phdrdata, preset.bank);
23
- writeWord(phdrdata, presetStart);
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(phdrdata, preset.library);
26
- writeDword(phdrdata, preset.genre);
27
- writeDword(phdrdata, preset.morphology);
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(phdrdata, "EOP", 20);
32
- writeWord(phdrdata, 0); // program
33
- writeWord(phdrdata, 0); // bank
34
- writeWord(phdrdata, presetStart);
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
- return writeRIFFChunk(new RiffChunk(
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
- phdrdata.length,
42
- phdrdata
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 {IndexedByteArray}
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 pmodsize = MOD_BYTE_SIZE;
14
+ let pmodSize = MOD_BYTE_SIZE;
15
15
  for (const preset of this.presets)
16
16
  {
17
- pmodsize += preset.globalZone.modulators.length * MOD_BYTE_SIZE;
18
- pmodsize += preset.presetZones.reduce((sum, z) => z.modulators.length * MOD_BYTE_SIZE + sum, 0);
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 pmoddata = new IndexedByteArray(pmodsize);
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(pmoddata, mod.getSourceEnum());
30
- writeWord(pmoddata, mod.modulatorDestination);
31
- writeWord(pmoddata, mod.transformAmount);
32
- writeWord(pmoddata, mod.getSecSrcEnum());
33
- writeWord(pmoddata, mod.transformType);
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(pmoddata, 0, MOD_BYTE_SIZE);
49
+ writeLittleEndian(pmodData, 0, MOD_BYTE_SIZE);
50
50
 
51
- return writeRIFFChunk(new RiffChunk(
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
- pmoddata.length,
54
- pmoddata
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 {IndexedByteArray}
11
+ * @returns {ReturnedExtendedSf2Chunks}
12
12
  */
13
13
  export function getSHDR(smplStartOffsets, smplEndOffsets)
14
14
  {
15
15
  const sampleLength = 46;
16
- const shdrData = new IndexedByteArray(sampleLength * (this.samples.length + 1)); // +1 because EOP
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
- return writeRIFFChunk(new RiffChunk(
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 pdtadata = combineArrays([
231
+ const pdtaData = combineArrays([
218
232
  new IndexedByteArray([112, 100, 116, 97]), // "pdta"
219
- phdrChunk,
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
- pdtadata.length,
232
- pdtadata
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
- data,
33
- sampleDbAttenuation
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.isCompressed)
226
+ if (this.dataOverriden)
227
+ {
228
+ return this.encodeS16LE();
229
+ }
230
+ else
65
231
  {
66
- if (!this.compressedData)
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
- throw new Error("Compressed but no data?? This shouldn't happen!!");
238
+ return this.rawData;
69
239
  }
70
- return this.compressedData;
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
  }