spessasynth_lib 3.17.0 → 3.20.0

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 (43) hide show
  1. package/@types/soundfont/basic_soundfont/basic_sample.d.ts +2 -2
  2. package/@types/soundfont/basic_soundfont/basic_zone.d.ts +12 -12
  3. package/@types/soundfont/basic_soundfont/basic_zones.d.ts +4 -0
  4. package/@types/soundfont/basic_soundfont/riff_chunk.d.ts +6 -0
  5. package/@types/soundfont/dls/articulator_converter.d.ts +10 -0
  6. package/@types/soundfont/dls/dls_destinations.d.ts +29 -0
  7. package/@types/soundfont/dls/dls_preset.d.ts +7 -5
  8. package/@types/soundfont/dls/dls_sample.d.ts +18 -0
  9. package/@types/soundfont/dls/dls_soundfont.d.ts +9 -2
  10. package/@types/soundfont/dls/dls_sources.d.ts +22 -0
  11. package/@types/soundfont/dls/dls_zone.d.ts +22 -0
  12. package/@types/soundfont/dls/read_articulation.d.ts +12 -0
  13. package/@types/soundfont/dls/read_instrument_list.d.ts +2 -2
  14. package/@types/soundfont/dls/read_lart.d.ts +7 -0
  15. package/@types/soundfont/dls/read_region.d.ts +7 -0
  16. package/@types/soundfont/dls/read_samples.d.ts +5 -0
  17. package/@types/soundfont/read_sf2/generators.d.ts +18 -5
  18. package/@types/soundfont/read_sf2/modulators.d.ts +1 -0
  19. package/README.md +10 -0
  20. package/midi_parser/midi_loader.js +30 -3
  21. package/package.json +1 -1
  22. package/soundfont/README.md +6 -2
  23. package/soundfont/basic_soundfont/basic_sample.js +3 -3
  24. package/soundfont/basic_soundfont/basic_zone.js +28 -28
  25. package/soundfont/basic_soundfont/basic_zones.js +15 -19
  26. package/soundfont/basic_soundfont/riff_chunk.js +18 -2
  27. package/soundfont/dls/articulator_converter.js +311 -0
  28. package/soundfont/dls/dls_destinations.js +38 -0
  29. package/soundfont/dls/dls_preset.js +15 -8
  30. package/soundfont/dls/dls_sample.js +58 -0
  31. package/soundfont/dls/dls_soundfont.js +68 -11
  32. package/soundfont/dls/dls_sources.js +26 -0
  33. package/soundfont/dls/dls_zone.js +75 -0
  34. package/soundfont/dls/read_articulation.js +327 -0
  35. package/soundfont/dls/read_instrument.js +82 -4
  36. package/soundfont/dls/read_instrument_list.js +6 -6
  37. package/soundfont/dls/read_lart.js +35 -0
  38. package/soundfont/dls/read_region.js +129 -0
  39. package/soundfont/dls/read_samples.js +174 -0
  40. package/soundfont/read_sf2/generators.js +41 -6
  41. package/soundfont/read_sf2/modulators.js +3 -3
  42. package/synthetizer/worklet_processor.min.js +10 -8
  43. package/utils/buffer_to_wav.js +5 -26
@@ -0,0 +1,327 @@
1
+ import { readLittleEndian } from '../../utils/byte_functions/little_endian.js'
2
+ import { DLSDestinations } from './dls_destinations.js'
3
+ import { Generator, generatorTypes } from '../read_sf2/generators.js'
4
+ import { DLSSources } from './dls_sources.js'
5
+ import { Modulator } from '../read_sf2/modulators.js'
6
+ import { getSF2ModulatorFromArticulator } from './articulator_converter.js'
7
+ import { SpessaSynthInfo, SpessaSynthWarn } from '../../utils/loggin.js'
8
+ import { consoleColors } from '../../utils/other.js'
9
+
10
+ /**
11
+ * @param source {number}
12
+ * @param control {number}
13
+ * @param destination {number}
14
+ * @param value {number}
15
+ * @param transform {number}
16
+ */
17
+ function modulatorConverterDebug(
18
+ source,
19
+ control,
20
+ destination,
21
+ value,
22
+ transform
23
+ )
24
+ {
25
+ const type = Object.keys(DLSDestinations).find(k => DLSDestinations[k] === destination);
26
+ const srcType = Object.keys(DLSSources).find(k => DLSSources[k] === source);
27
+ const ctrlType = Object.keys(DLSSources).find(k => DLSSources[k] === control);
28
+ const typeString = type ? type : destination.toString(16);
29
+ const srcString = srcType ? srcType : source.toString(16);
30
+ const ctrlString = ctrlType ? ctrlType : control.toString(16);
31
+ SpessaSynthInfo(
32
+ `%cAttempting to convert the following DLS Articulator to SF2 Modulator:
33
+ Source: %c${srcString}%c
34
+ Control: %c${ctrlString}%c
35
+ Destination: %c${typeString}%c
36
+ Amount: %c${value}%c
37
+ Transform: %c${transform}%c...`,
38
+ consoleColors.info,
39
+ consoleColors.recognized,
40
+ consoleColors.info,
41
+ consoleColors.recognized,
42
+ consoleColors.info,
43
+ consoleColors.recognized,
44
+ consoleColors.info,
45
+ consoleColors.recognized,
46
+ consoleColors.info,
47
+ consoleColors.recognized,
48
+ consoleColors.info);
49
+ }
50
+
51
+ /**
52
+ * Reads the articulator chunk
53
+ * @param chunk {RiffChunk}
54
+ * @param disableVibrato {boolean} it seems that dls 1 does not have vibrato lfo, so we shall disable it
55
+ * @returns {{modulators: Modulator[], generators: Generator[]}}
56
+ */
57
+ export function readArticulation(chunk, disableVibrato)
58
+ {
59
+ const artData = chunk.chunkData;
60
+ /**
61
+ * @type {Generator[]}
62
+ */
63
+ const generators = [];
64
+ /**
65
+ * @type {Modulator[]}
66
+ */
67
+ const modulators = [];
68
+
69
+ // cbSize (ignore)
70
+ readLittleEndian(artData, 4);
71
+ const connectionsAmount = readLittleEndian(artData, 4);
72
+ for (let i = 0; i < connectionsAmount; i++)
73
+ {
74
+ // read the block
75
+ const source = readLittleEndian(artData, 2);
76
+ const control = readLittleEndian(artData, 2);
77
+ const destination = readLittleEndian(artData, 2);
78
+ const transform = readLittleEndian(artData, 2);
79
+ const value = readLittleEndian(artData, 4) >> 16; // convert it to 16 bit as soundfont uses that
80
+
81
+ // interpret this somehow...
82
+ // if source and control are both zero, it's a generator
83
+ if(source === 0 && control === 0 && transform === 0)
84
+ {
85
+ /**
86
+ * @type {Generator}
87
+ */
88
+ let generator;
89
+ switch(destination)
90
+ {
91
+ case DLSDestinations.pan:
92
+ generator = new Generator(generatorTypes.pan, value); // turn percent into tenths of percent
93
+ break;
94
+ case DLSDestinations.gain:
95
+ generator = new Generator(generatorTypes.initialAttenuation, -value * 10 / 0.4); // turn to centibels and apply emu correction
96
+ break;
97
+ case DLSDestinations.filterCutoff:
98
+ generator = new Generator(generatorTypes.initialFilterFc, value);
99
+ break;
100
+ case DLSDestinations.filterQ:
101
+ generator = new Generator(generatorTypes.initialFilterQ, value);
102
+ break;
103
+
104
+ // mod lfo raw values it seems
105
+ case DLSDestinations.modLfoFreq:
106
+ generator = new Generator(generatorTypes.freqModLFO, value);
107
+ break;
108
+ case DLSDestinations.modLfoDelay:
109
+ generator = new Generator(generatorTypes.delayModLFO, value);
110
+ break;
111
+ case DLSDestinations.vibLfoFreq:
112
+ generator = new Generator(generatorTypes.freqVibLFO, value);
113
+ break;
114
+ case DLSDestinations.vibLfoDelay:
115
+ generator = new Generator(generatorTypes.delayVibLFO, value);
116
+ break;
117
+
118
+ // vol env: all times are timecents like sf2
119
+ case DLSDestinations.volEnvDelay:
120
+ generator = new Generator(generatorTypes.delayVolEnv, value);
121
+ break;
122
+ case DLSDestinations.volEnvAttack:
123
+ generator = new Generator(generatorTypes.attackVolEnv, value);
124
+ break;
125
+ case DLSDestinations.volEnvHold:
126
+ generator = new Generator(generatorTypes.holdVolEnv, value);
127
+ break;
128
+ case DLSDestinations.volEnvDecay:
129
+ generator = new Generator(generatorTypes.decayVolEnv, value);
130
+ break;
131
+ case DLSDestinations.volEnvRelease:
132
+ generator = new Generator(generatorTypes.releaseVolEnv, value);
133
+ break;
134
+ case DLSDestinations.volEnvSustain:
135
+ // gain seems to be (1000 - value) / 10 = sustain dB
136
+ const sustainDb = (1000 - value) / 10;
137
+ generator = new Generator(generatorTypes.sustainVolEnv, sustainDb * 10);
138
+ break;
139
+
140
+ // mod env
141
+ case DLSDestinations.modEnvDelay:
142
+ generator = new Generator(generatorTypes.delayModEnv, value);
143
+ break;
144
+ case DLSDestinations.modEnvAttack:
145
+ generator = new Generator(generatorTypes.attackModEnv, value);
146
+ break;
147
+ case DLSDestinations.modEnvHold:
148
+ generator = new Generator(generatorTypes.holdModEnv, value);
149
+ break;
150
+ case DLSDestinations.modEnvDecay:
151
+ generator = new Generator(generatorTypes.decayModEnv, value);
152
+ break;
153
+ case DLSDestinations.modEnvRelease:
154
+ generator = new Generator(generatorTypes.releaseModEnv, value);
155
+ break;
156
+ case DLSDestinations.modEnvSustain:
157
+ // dls uses 1%, desfont uses 0.1%
158
+ const percentageSustain = 1000 - value;
159
+ generator = new Generator(generatorTypes.sustainModEnv, percentageSustain);
160
+ break;
161
+
162
+ case DLSDestinations.reverbSend:
163
+ generator = new Generator(generatorTypes.reverbEffectsSend, value);
164
+ break;
165
+ case DLSDestinations.chorusSend:
166
+ generator = new Generator(generatorTypes.chorusEffectsSend, value);
167
+ break;
168
+ case DLSDestinations.pitch:
169
+ // split it up
170
+ const semi = Math.floor(value / 100);
171
+ const cents = Math.floor(value - semi * 100);
172
+ generator = new Generator(generatorTypes.fineTune, cents);
173
+ generators.push(new Generator(generatorTypes.coarseTune, semi));
174
+ break;
175
+ }
176
+ if(generator)
177
+ {
178
+ generators.push(generator);
179
+ }
180
+ }
181
+ else
182
+ // if not, modulator??
183
+ {
184
+ let isGenerator = true;
185
+ // a few special cases which are generators:
186
+ if(control === DLSSources.none)
187
+ {
188
+ // mod lfo to pitch
189
+ if(source === DLSSources.modLfo && destination === DLSDestinations.pitch)
190
+ {
191
+ generators.push(new Generator(generatorTypes.modLfoToPitch, value));
192
+ }
193
+ else
194
+ // mod lfo to volume
195
+ if(source === DLSSources.modLfo && destination === DLSDestinations.gain)
196
+ {
197
+ generators.push(new Generator(generatorTypes.modLfoToVolume, value));
198
+ }
199
+ else
200
+ // mod lfo to filter
201
+ if(source === DLSSources.modLfo && destination === DLSDestinations.filterCutoff)
202
+ {
203
+ generators.push(new Generator(generatorTypes.modLfoToFilterFc, value));
204
+ }
205
+ else
206
+ // vib lfo to pitch
207
+ if(source === DLSSources.vibratoLfo && destination === DLSDestinations.pitch)
208
+ {
209
+ generators.push(new Generator(generatorTypes.vibLfoToPitch, value));
210
+ }
211
+ else
212
+ // mod env to pitch
213
+ if(source === DLSSources.modEnv && destination === DLSDestinations.pitch)
214
+ {
215
+ generators.push(new Generator(generatorTypes.modEnvToPitch, value));
216
+ }
217
+ else
218
+ // mod env to filter
219
+ if(source === DLSSources.modEnv && destination === DLSDestinations.filterCutoff)
220
+ {
221
+ generators.push(new Generator(generatorTypes.modEnvToFilterFc, value));
222
+ }
223
+ else
224
+ // key to vol env hold
225
+ if(source === DLSSources.keyNum && destination === DLSDestinations.volEnvHold)
226
+ {
227
+ // according to viena and another strange (with modulators) rendition of gm.dls in sf2, this is how it should be done???
228
+ generators.push(new Generator(generatorTypes.keyNumToVolEnvHold, value / -127));
229
+ }
230
+ else
231
+ // key to vol env decay
232
+ if(source === DLSSources.keyNum && destination === DLSDestinations.volEnvDecay)
233
+ {
234
+ generators.push(new Generator(generatorTypes.keyNumToVolEnvDecay, value / -127));
235
+ }
236
+ else
237
+ // key to mod env hold
238
+ if(source === DLSSources.keyNum && destination === DLSDestinations.modEnvHold)
239
+ {
240
+ generators.push(new Generator(generatorTypes.keyNumToModEnvHold, value / -127));
241
+ }
242
+ else
243
+ // key to mod env decay
244
+ if(source === DLSSources.keyNum && destination === DLSDestinations.modEnvDecay)
245
+ {
246
+ generators.push(new Generator(generatorTypes.keyNumToModEnvDecay, value / -127));
247
+ }
248
+ else
249
+ {
250
+ isGenerator = false;
251
+ }
252
+
253
+ }
254
+ else
255
+ {
256
+ isGenerator = false;
257
+ }
258
+ if(isGenerator === false)
259
+ {
260
+ // UNCOMMENT TO ENABLE DEBUG
261
+ // modulatorConverterDebug(source, control, destination, value, transform)
262
+ // convert it to modulator
263
+ const mod = getSF2ModulatorFromArticulator(
264
+ source,
265
+ control,
266
+ destination,
267
+ transform,
268
+ value
269
+ );
270
+ if(mod)
271
+ {
272
+ // some articulators cannot be turned into modulators, that's why this check is a thing
273
+ modulators.push(mod);
274
+ SpessaSynthInfo("%cSucceeded converting to SF2 Modulator!", consoleColors.recognized)
275
+ }
276
+ else
277
+ {
278
+ SpessaSynthWarn("Failed converting to SF2 Modulator!")
279
+ }
280
+ }
281
+ }
282
+ }
283
+
284
+ // override reverb and chorus with 1000 instead of 200
285
+ modulators.push(
286
+ new Modulator({
287
+ srcEnum: 0x00DB,
288
+ dest: generatorTypes.reverbEffectsSend,
289
+ amt: 1000,
290
+ secSrcEnum: 0x0,
291
+ transform: 0
292
+ }),
293
+ // chorus effects to send
294
+ new Modulator({
295
+ srcEnum: 0x00DD,
296
+ dest: generatorTypes.chorusEffectsSend,
297
+ amt: 1000,
298
+ secSrcEnum: 0x0,
299
+ transform: 0
300
+ }),
301
+ )
302
+
303
+ // it seems that dls 1 does not have vibrato lfo, so we shall disable it
304
+ if(disableVibrato)
305
+ {
306
+ modulators.push(
307
+ // mod to vib
308
+ new Modulator({
309
+ srcEnum: 0x0081,
310
+ dest: generatorTypes.vibLfoToPitch,
311
+ amt: 0,
312
+ secSrcEnum: 0x0,
313
+ transform: 0
314
+ }),
315
+ // press to vib
316
+ new Modulator({
317
+ srcEnum: 0x000D,
318
+ dest: generatorTypes.vibLfoToPitch,
319
+ amt: 0,
320
+ secSrcEnum: 0x0,
321
+ transform: 0
322
+ })
323
+ );
324
+ }
325
+
326
+ return {modulators: modulators, generators: generators}
327
+ }
@@ -1,7 +1,10 @@
1
1
  import { readBytesAsString } from '../../utils/byte_functions/string.js'
2
2
  import { readLittleEndian } from '../../utils/byte_functions/little_endian.js'
3
3
  import { DLSPreset } from './dls_preset.js'
4
- import { readRIFFChunk } from '../basic_soundfont/riff_chunk.js'
4
+ import { findRIFFListType, readRIFFChunk } from '../basic_soundfont/riff_chunk.js'
5
+ import { SpessaSynthGroup, SpessaSynthGroupEnd } from '../../utils/loggin.js'
6
+ import { BasicInstrumentZone } from '../basic_soundfont/basic_zones.js'
7
+ import { consoleColors } from '../../utils/other.js'
5
8
 
6
9
  /**
7
10
  * @this {DLSSoundFont}
@@ -11,12 +14,87 @@ export function readDLSInstrument(chunk)
11
14
  {
12
15
  this.verifyHeader(chunk, "LIST");
13
16
  this.verifyText(readBytesAsString(chunk.chunkData, 4), "ins ");
17
+ /**
18
+ * @type {RiffChunk[]}
19
+ */
20
+ const chunks = [];
21
+ while(chunk.chunkData.length > chunk.chunkData.currentIndex)
22
+ {
23
+ chunks.push(readRIFFChunk(chunk.chunkData));
24
+ }
14
25
 
15
- const instrumentHeader = readRIFFChunk(chunk.chunkData);
16
- this.verifyHeader(instrumentHeader, "insh");
17
26
 
27
+ const instrumentHeader = chunks.find(c => c.header === "insh");
28
+ if(!instrumentHeader)
29
+ {
30
+ SpessaSynthGroupEnd();
31
+ throw new Error("No instrument header!");
32
+ }
33
+
34
+ // read instrument header
18
35
  const regions = readLittleEndian(instrumentHeader.chunkData, 4);
19
36
  const ulBank = readLittleEndian(instrumentHeader.chunkData, 4);
20
37
  const ulInstrument = readLittleEndian(instrumentHeader.chunkData, 4);
21
- this.presets.push(new DLSPreset(ulBank, ulInstrument, regions));
38
+ const preset = new DLSPreset(ulBank, ulInstrument);
39
+
40
+ // read preset name in INFO
41
+ let presetName = "unnamedPreset";
42
+ const infoChunk = findRIFFListType(chunks, "INFO");
43
+ if(infoChunk)
44
+ {
45
+ let info = readRIFFChunk(infoChunk.chunkData);
46
+ while(info.header !== "INAM")
47
+ {
48
+ info = readRIFFChunk(infoChunk.chunkData);
49
+ }
50
+ presetName = readBytesAsString(info.chunkData, info.chunkData.length).trim();
51
+ }
52
+ preset.presetName = presetName;
53
+ preset.DLSInstrument.instrumentName = presetName;
54
+ SpessaSynthGroup(`%cParsing %c"${presetName}"%c...`,
55
+ consoleColors.info,
56
+ consoleColors.recognized,
57
+ consoleColors.info);
58
+
59
+ // list of regions
60
+ const regionListChunk = findRIFFListType(chunks, "lrgn");
61
+ if(!regionListChunk)
62
+ {
63
+ SpessaSynthGroupEnd();
64
+ throw new Error("No region list!");
65
+ }
66
+
67
+ // global articulation: essentially global zone
68
+ const globalZone = new BasicInstrumentZone();
69
+ globalZone.isGlobal = true;
70
+
71
+ // read articulators
72
+ const globalLart = findRIFFListType(chunks, "lart");
73
+ const globalLar2 = findRIFFListType(chunks, "lar2");
74
+ this.readLart(globalLart, globalLar2, globalZone);
75
+ preset.DLSInstrument.instrumentZones.push(globalZone);
76
+
77
+ // read regions
78
+ for (let i = 0; i < regions; i++)
79
+ {
80
+ const chunk = readRIFFChunk(regionListChunk.chunkData);
81
+ this.verifyHeader(chunk, "LIST");
82
+ const type = readBytesAsString(chunk.chunkData, 4);
83
+ if(type !== "rgn " && type !== "rgn2")
84
+ {
85
+ SpessaSynthGroupEnd();
86
+ throw new SyntaxError(`Invalid DLS region! Expected "rgn " or "rgn2" got "${type}"`);
87
+ }
88
+
89
+
90
+ const zone = this.readRegion(chunk);
91
+ if(zone)
92
+ {
93
+ preset.DLSInstrument.instrumentZones.push(zone);
94
+ }
95
+ }
96
+
97
+ this.presets.push(preset);
98
+ this.instruments.push(preset.DLSInstrument);
99
+ SpessaSynthGroupEnd();
22
100
  }
@@ -1,17 +1,17 @@
1
1
  import { readRIFFChunk } from '../basic_soundfont/riff_chunk.js'
2
- import { readBytesAsString } from '../../utils/byte_functions/string.js'
2
+ import { SpessaSynthGroupCollapsed, SpessaSynthGroupEnd } from '../../utils/loggin.js'
3
+ import { consoleColors } from '../../utils/other.js'
3
4
 
4
5
  /**
5
6
  * @this {DLSSoundFont}
6
- * @param dataArray {IndexedByteArray}
7
+ * @param instrumentListChunk {RiffChunk}
7
8
  */
8
- export function readDLSInstrumentList(dataArray)
9
+ export function readDLSInstrumentList(instrumentListChunk)
9
10
  {
10
- const instrumentListChunk = readRIFFChunk(dataArray);
11
- this.verifyHeader(instrumentListChunk, "LIST");
12
- this.verifyText(readBytesAsString(instrumentListChunk.chunkData, 4), "lins");
11
+ SpessaSynthGroupCollapsed("%cLoading instruments...", consoleColors.info);
13
12
  for(let i = 0; i < this.instrumentAmount; i++)
14
13
  {
15
14
  this.readDLSInstrument(readRIFFChunk(instrumentListChunk.chunkData));
16
15
  }
16
+ SpessaSynthGroupEnd();
17
17
  }
@@ -0,0 +1,35 @@
1
+ import { readRIFFChunk } from '../basic_soundfont/riff_chunk.js'
2
+ import { readArticulation } from './read_articulation.js'
3
+
4
+ /**
5
+ * @param lartChunk {RiffChunk|undefined}
6
+ * @param lar2Chunk {RiffChunk|undefined}
7
+ * @param zone {BasicInstrumentZone}
8
+ * @this {DLSSoundFont}
9
+ */
10
+ export function readLart(lartChunk, lar2Chunk, zone)
11
+ {
12
+ if(lartChunk)
13
+ {
14
+ while(lartChunk.chunkData.currentIndex < lartChunk.chunkData.length)
15
+ {
16
+ const art1 = readRIFFChunk(lartChunk.chunkData);
17
+ this.verifyHeader(art1, "art1");
18
+ const modsAndGens = readArticulation(art1, true);
19
+ zone.generators.push(...modsAndGens.generators);
20
+ zone.modulators.push(...modsAndGens.modulators);
21
+ }
22
+ }
23
+
24
+ if(lar2Chunk)
25
+ {
26
+ while(lar2Chunk.chunkData.currentIndex < lar2Chunk.chunkData.length)
27
+ {
28
+ const art2 = readRIFFChunk(lar2Chunk.chunkData);
29
+ this.verifyHeader(art2, "art2");
30
+ const modsAndGens = readArticulation(art2, false);
31
+ zone.generators.push(...modsAndGens.generators);
32
+ zone.modulators.push(...modsAndGens.modulators);
33
+ }
34
+ }
35
+ }
@@ -0,0 +1,129 @@
1
+ import { readLittleEndian } from '../../utils/byte_functions/little_endian.js'
2
+ import { findRIFFListType, readRIFFChunk } from '../basic_soundfont/riff_chunk.js'
3
+ import { Generator, generatorTypes } from '../read_sf2/generators.js'
4
+ import { DLSZone } from './dls_zone.js'
5
+
6
+ /**
7
+ * @this {DLSSoundFont}
8
+ * @param chunk {RiffChunk}
9
+ * @returns {DLSZone}
10
+ */
11
+ export function readRegion(chunk)
12
+ {
13
+ // regions are essentially instrument zones
14
+
15
+ /**
16
+ * read chunks in the region
17
+ * @type {RiffChunk[]}
18
+ */
19
+ const regionChunks = [];
20
+ while(chunk.chunkData.length > chunk.chunkData.currentIndex)
21
+ {
22
+ regionChunks.push(readRIFFChunk(chunk.chunkData));
23
+ }
24
+
25
+ // region header
26
+ const regionHeader = regionChunks.find(c => c.header === "rgnh");
27
+ // key range
28
+ const keyMin = readLittleEndian(regionHeader.chunkData, 2);
29
+ const keyMax = readLittleEndian(regionHeader.chunkData, 2);
30
+ // vel range
31
+ const velMin = readLittleEndian(regionHeader.chunkData, 2);
32
+ const velMax = readLittleEndian(regionHeader.chunkData, 2);
33
+
34
+ const zone = new DLSZone(
35
+ {min: keyMin, max: keyMax},
36
+ {min: velMin, max: velMax}
37
+ )
38
+
39
+ // fusOptions: no idea about that one???
40
+ readLittleEndian(regionHeader.chunkData, 2);
41
+
42
+ // keyGroup: essentially exclusive class
43
+ const exclusive = readLittleEndian(regionHeader.chunkData, 2);
44
+ if(exclusive !== 0)
45
+ {
46
+ zone.generators.push(new Generator(generatorTypes.exclusiveClass, exclusive));
47
+ }
48
+
49
+ // lart
50
+ const lart = findRIFFListType(regionChunks, "lart");
51
+ const lar2 = findRIFFListType(regionChunks, "lar2");
52
+ this.readLart(lart, lar2, zone);
53
+
54
+ // wsmpl: wave sample chunk
55
+ zone.isGlobal = false;
56
+ const waveSampleChunk = regionChunks.find(c => c.header === "wsmp");
57
+ // cbSize
58
+ readLittleEndian(waveSampleChunk.chunkData, 4);
59
+ const originalKey = readLittleEndian(waveSampleChunk.chunkData, 2);
60
+ // pitch correction is read from the wave wsmpl chunk
61
+ readLittleEndian(waveSampleChunk.chunkData, 2);
62
+
63
+ // gain correction: Each unit of gain represents 1/655360 dB
64
+ const gainCorrection = readLittleEndian(waveSampleChunk.chunkData, 4);
65
+ // convert to signed and turn into attenuation (invert)
66
+ const dbCorrection = (gainCorrection | 0) / -655360;
67
+ // convert to centibels
68
+ const attenuation = (dbCorrection * 10) / 0.4; // make sure to apply EMU correction
69
+
70
+ // skip options
71
+ readLittleEndian(waveSampleChunk.chunkData, 4);
72
+
73
+ // read loop count (always one or zero)
74
+ const loopsAmount = readLittleEndian(waveSampleChunk.chunkData, 4);
75
+ let loopingMode;
76
+ const loop = {start: 0, end: 0};
77
+ if(loopsAmount === 0)
78
+ {
79
+ // no loop
80
+ loopingMode = 0;
81
+ }
82
+ else
83
+ {
84
+ // ignore cbSize
85
+ readLittleEndian(waveSampleChunk.chunkData, 4);
86
+ // loop type: loop normally or loop until release (like soundfont)
87
+ const loopType = readLittleEndian(waveSampleChunk.chunkData, 4); // why is it long???
88
+ if(loopType === 0)
89
+ {
90
+ loopingMode = 1;
91
+ }
92
+ else
93
+ {
94
+ loopingMode = 3;
95
+ }
96
+ loop.start = readLittleEndian(waveSampleChunk.chunkData, 4);
97
+ const loopLength = readLittleEndian(waveSampleChunk.chunkData, 4);
98
+ loop.end = loop.start + loopLength;
99
+ }
100
+
101
+ // wave link
102
+ const waveLinkChunk = regionChunks.find(c => c.header === "wlnk");
103
+ if(waveLinkChunk === undefined)
104
+ {
105
+ // no wave link = no sample. What? Why is it even here then????
106
+ return undefined;
107
+ }
108
+
109
+ // flags
110
+ readLittleEndian(waveLinkChunk.chunkData, 2);
111
+ // phasse group
112
+ readLittleEndian(waveLinkChunk.chunkData, 2);
113
+ // channel
114
+ readLittleEndian(waveLinkChunk.chunkData, 4);
115
+ // sampleID
116
+ const sampleID = readLittleEndian(waveLinkChunk.chunkData, 4);
117
+ const sample = this.samples[sampleID];
118
+ if(sample === undefined)
119
+ {
120
+ throw new Error("Invalid sample ID!");
121
+ }
122
+ zone.setWavesample(
123
+ attenuation, loopingMode,
124
+ loop,
125
+ originalKey,
126
+ sample,
127
+ sampleID);
128
+ return zone;
129
+ }