spessasynth_lib 3.22.12 → 3.23.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 (52) hide show
  1. package/@types/soundfont/basic_soundfont/basic_soundfont.d.ts +2 -0
  2. package/@types/soundfont/basic_soundfont/basic_zone.d.ts +16 -0
  3. package/@types/soundfont/basic_soundfont/generator.d.ts +2 -1
  4. package/@types/soundfont/basic_soundfont/riff_chunk.d.ts +2 -1
  5. package/@types/soundfont/basic_soundfont/write_dls/art2.d.ts +6 -0
  6. package/@types/soundfont/basic_soundfont/write_dls/articulator.d.ts +28 -0
  7. package/@types/soundfont/basic_soundfont/write_dls/combine_zones.d.ts +8 -0
  8. package/@types/soundfont/basic_soundfont/write_dls/ins.d.ts +7 -0
  9. package/@types/soundfont/basic_soundfont/write_dls/lins.d.ts +5 -0
  10. package/@types/soundfont/basic_soundfont/write_dls/modulator_converter.d.ts +11 -0
  11. package/@types/soundfont/basic_soundfont/write_dls/rgn2.d.ts +7 -0
  12. package/@types/soundfont/basic_soundfont/write_dls/wave.d.ts +6 -0
  13. package/@types/soundfont/basic_soundfont/write_dls/write_dls.d.ts +6 -0
  14. package/@types/soundfont/basic_soundfont/write_dls/wsmp.d.ts +12 -0
  15. package/@types/soundfont/basic_soundfont/write_dls/wvpl.d.ts +8 -0
  16. package/midi_parser/midi_editor.js +0 -0
  17. package/package.json +1 -1
  18. package/soundfont/basic_soundfont/basic_preset.js +23 -12
  19. package/soundfont/basic_soundfont/basic_soundfont.js +2 -0
  20. package/soundfont/basic_soundfont/basic_zone.js +30 -5
  21. package/soundfont/basic_soundfont/generator.js +10 -4
  22. package/soundfont/basic_soundfont/riff_chunk.js +20 -5
  23. package/soundfont/basic_soundfont/write_dls/art2.js +136 -0
  24. package/soundfont/basic_soundfont/write_dls/articulator.js +49 -0
  25. package/soundfont/basic_soundfont/write_dls/combine_zones.js +398 -0
  26. package/soundfont/basic_soundfont/write_dls/ins.js +103 -0
  27. package/soundfont/basic_soundfont/write_dls/lins.js +18 -0
  28. package/soundfont/basic_soundfont/write_dls/modulator_converter.js +324 -0
  29. package/soundfont/basic_soundfont/write_dls/rgn2.js +101 -0
  30. package/soundfont/basic_soundfont/write_dls/wave.js +74 -0
  31. package/soundfont/basic_soundfont/write_dls/write_dls.js +118 -0
  32. package/soundfont/basic_soundfont/write_dls/wsmp.js +74 -0
  33. package/soundfont/basic_soundfont/write_dls/wvpl.js +32 -0
  34. package/soundfont/basic_soundfont/write_sf2/igen.js +3 -3
  35. package/soundfont/basic_soundfont/write_sf2/pgen.js +2 -2
  36. package/soundfont/dls/articulator_converter.js +10 -0
  37. package/soundfont/dls/dls_sample.js +2 -2
  38. package/soundfont/dls/dls_soundfont.js +2 -1
  39. package/soundfont/dls/dls_zone.js +21 -18
  40. package/soundfont/dls/read_articulation.js +18 -30
  41. package/soundfont/dls/read_instrument.js +31 -1
  42. package/soundfont/dls/read_region.js +0 -4
  43. package/soundfont/dls/read_samples.js +17 -17
  44. package/synthetizer/key_modifier_manager.js +1 -1
  45. package/synthetizer/worklet_processor.min.js +13 -12
  46. package/synthetizer/worklet_system/main_processor.js +2 -0
  47. package/synthetizer/worklet_system/worklet_methods/note_on.js +2 -1
  48. package/synthetizer/worklet_system/worklet_methods/reset_controllers.js +4 -12
  49. package/synthetizer/worklet_system/worklet_methods/voice_control.js +4 -3
  50. package/synthetizer/worklet_system/worklet_methods/worklet_soundfont_manager/worklet_soundfont_manager.js +0 -0
  51. package/synthetizer/worklet_system/worklet_utilities/worklet_voice.js +1 -1
  52. package/utils/byte_functions/string.js +1 -1
@@ -0,0 +1,324 @@
1
+ import { midiControllers } from "../../../midi_parser/midi_message.js";
2
+ import { DLSSources } from "../../dls/dls_sources.js";
3
+ import { modulatorCurveTypes, modulatorSources } from "../modulator.js";
4
+ import { generatorTypes } from "../generator.js";
5
+ import { DLSDestinations } from "../../dls/dls_destinations.js";
6
+ import { Articulator } from "./articulator.js";
7
+ import { SpessaSynthWarn } from "../../../utils/loggin.js";
8
+
9
+
10
+ /**
11
+ * @param cc {boolean}
12
+ * @param index {number}
13
+ * @returns {number|undefined}
14
+ */
15
+ function getDLSSourceFromSf2Source(cc, index)
16
+ {
17
+ if (cc)
18
+ {
19
+ switch (index)
20
+ {
21
+ default:
22
+ // DLS supports limited controllers
23
+ return undefined;
24
+
25
+ case midiControllers.modulationWheel:
26
+ return DLSSources.modulationWheel;
27
+ case midiControllers.mainVolume:
28
+ return DLSSources.volume;
29
+ case midiControllers.pan:
30
+ return DLSSources.pan;
31
+ case midiControllers.expressionController:
32
+ return DLSSources.expression;
33
+ case midiControllers.chorusDepth:
34
+ return DLSSources.chorus;
35
+ case midiControllers.reverbDepth:
36
+ return DLSSources.reverb;
37
+ }
38
+ }
39
+ else
40
+ {
41
+ switch (index)
42
+ {
43
+ default:
44
+ // cannot be a DLS articulator
45
+ return undefined;
46
+
47
+ case modulatorSources.noteOnKeyNum:
48
+ return DLSSources.keyNum;
49
+ case modulatorSources.noteOnVelocity:
50
+ return DLSSources.velocity;
51
+ case modulatorSources.noController:
52
+ return DLSSources.none;
53
+ case modulatorSources.polyPressure:
54
+ return DLSSources.polyPressure;
55
+ case modulatorSources.channelPressure:
56
+ return DLSSources.channelPressure;
57
+ case modulatorSources.pitchWheel:
58
+ return DLSSources.pitchWheel;
59
+ case modulatorSources.pitchWheelRange:
60
+ return DLSSources.pitchWheelRange;
61
+ }
62
+ }
63
+ }
64
+
65
+ /**
66
+ * @param dest {number}
67
+ * @param amount {number}
68
+ * @returns {number|undefined|{dest: number, amount: number}}
69
+ */
70
+ function getDLSDestinationFromSf2(dest, amount)
71
+ {
72
+ switch (dest)
73
+ {
74
+ default:
75
+ return undefined;
76
+
77
+ case generatorTypes.initialAttenuation:
78
+ // amount does not get EMU corrected here, as this only applies to modulator attenuation
79
+ // the generator (affected) attenuation is handled in wsmp.
80
+ return { dest: DLSDestinations.gain, amount: -amount };
81
+ case generatorTypes.fineTune:
82
+ return DLSDestinations.pitch;
83
+ case generatorTypes.pan:
84
+ return DLSDestinations.pan;
85
+ case generatorTypes.keyNum:
86
+ return DLSDestinations.keyNum;
87
+
88
+ case generatorTypes.reverbEffectsSend:
89
+ return DLSDestinations.reverbSend;
90
+ case generatorTypes.chorusEffectsSend:
91
+ return DLSDestinations.chorusSend;
92
+
93
+ case generatorTypes.freqModLFO:
94
+ return DLSDestinations.modLfoFreq;
95
+ case generatorTypes.delayModLFO:
96
+ return DLSDestinations.modLfoDelay;
97
+
98
+ case generatorTypes.delayVibLFO:
99
+ return DLSDestinations.vibLfoDelay;
100
+ case generatorTypes.freqVibLFO:
101
+ return DLSDestinations.vibLfoFreq;
102
+
103
+ case generatorTypes.delayVolEnv:
104
+ return DLSDestinations.volEnvDelay;
105
+ case generatorTypes.attackVolEnv:
106
+ return DLSDestinations.volEnvAttack;
107
+ case generatorTypes.holdVolEnv:
108
+ return DLSDestinations.volEnvHold;
109
+ case generatorTypes.decayVolEnv:
110
+ return DLSDestinations.volEnvDecay;
111
+ case generatorTypes.sustainVolEnv:
112
+ return { dest: DLSDestinations.volEnvSustain, amount: 1000 - amount };
113
+ case generatorTypes.releaseVolEnv:
114
+ return DLSDestinations.volEnvRelease;
115
+
116
+ case generatorTypes.delayModEnv:
117
+ return DLSDestinations.modEnvDelay;
118
+ case generatorTypes.attackModEnv:
119
+ return DLSDestinations.modEnvAttack;
120
+ case generatorTypes.holdModEnv:
121
+ return DLSDestinations.modEnvHold;
122
+ case generatorTypes.decayModEnv:
123
+ return DLSDestinations.modEnvDecay;
124
+ case generatorTypes.sustainModEnv:
125
+ return { dest: DLSDestinations.modEnvSustain, amount: 1000 - amount };
126
+ case generatorTypes.releaseModEnv:
127
+ return DLSDestinations.modEnvRelease;
128
+
129
+ case generatorTypes.initialFilterFc:
130
+ return DLSDestinations.filterCutoff;
131
+ case generatorTypes.initialFilterQ:
132
+ return DLSDestinations.filterQ;
133
+ }
134
+ }
135
+
136
+ /**
137
+ * @param dest {number}
138
+ * @param amt {number}
139
+ * @returns {{source: DLSSources, dest: DLSDestinations, amt: number, isBipolar: boolean}|undefined}
140
+ */
141
+ function checkSF2SpecialCombos(dest, amt)
142
+ {
143
+
144
+ switch (dest)
145
+ {
146
+ default:
147
+ return undefined;
148
+ // mod env
149
+ case generatorTypes.modEnvToFilterFc:
150
+ return { source: DLSSources.modEnv, dest: DLSDestinations.filterCutoff, amt: amt, isBipolar: false };
151
+ case generatorTypes.modEnvToPitch:
152
+ return { source: DLSSources.modEnv, dest: DLSDestinations.pitch, amt: amt, isBipolar: false };
153
+
154
+ // mod lfo
155
+ case generatorTypes.modLfoToFilterFc:
156
+ return { source: DLSSources.modLfo, dest: DLSDestinations.filterCutoff, amt: amt, isBipolar: true };
157
+ case generatorTypes.modLfoToVolume:
158
+ return { source: DLSSources.modLfo, dest: DLSDestinations.gain, amt: amt, isBipolar: true };
159
+ case generatorTypes.modLfoToPitch:
160
+ return { source: DLSSources.modLfo, dest: DLSDestinations.pitch, amt: amt, isBipolar: true };
161
+
162
+ // vib lfo
163
+ case generatorTypes.vibLfoToPitch:
164
+ return { source: DLSSources.vibratoLfo, dest: DLSDestinations.pitch, amt: amt, isBipolar: true };
165
+
166
+ // key to something
167
+ case generatorTypes.keyNumToVolEnvHold:
168
+ return {
169
+ source: DLSSources.keyNum,
170
+ dest: DLSDestinations.volEnvHold,
171
+ amt: amt,
172
+ isBipolar: true
173
+ };
174
+ case generatorTypes.keyNumToVolEnvDecay:
175
+ return {
176
+ source: DLSSources.keyNum,
177
+ dest: DLSDestinations.volEnvDecay,
178
+ amt: amt,
179
+ isBipolar: true
180
+ };
181
+ case generatorTypes.keyNumToModEnvHold:
182
+ return {
183
+ source: DLSSources.keyNum,
184
+ dest: DLSDestinations.modEnvHold,
185
+ amt: amt,
186
+ isBipolar: true
187
+ };
188
+ case generatorTypes.keyNumToModEnvDecay:
189
+ return {
190
+ source: DLSSources.keyNum,
191
+ dest: DLSDestinations.modEnvDecay,
192
+ amt: amt,
193
+ isBipolar: true
194
+ };
195
+
196
+ // scale tuning is implemented in DLS via an articulator:
197
+ // keyNum to relative pitch at 12800 cents.
198
+ // Change that to scale tuning * 128.
199
+ // therefore regular scale is still 12800, half is 6400 etc.
200
+ case generatorTypes.scaleTuning:
201
+ return {
202
+ source: DLSSources.keyNum,
203
+ dest: DLSDestinations.pitch,
204
+ amt: amt * 128,
205
+ isBipolar: false // according to table 4, this should be false.
206
+ };
207
+ }
208
+ }
209
+
210
+ /**
211
+ * @param gen {Generator}
212
+ * @returns {Articulator|undefined}
213
+ */
214
+ export function getDLSArticulatorFromSf2Generator(gen)
215
+ {
216
+ const dest = getDLSDestinationFromSf2(gen.generatorType, gen.generatorValue);
217
+ let destination = dest;
218
+ let source = 0;
219
+ let amount = gen.generatorValue;
220
+ if (dest?.amount !== undefined)
221
+ {
222
+ amount = dest.amount;
223
+ destination = dest.dest;
224
+ }
225
+ // check for special combo
226
+ const combo = checkSF2SpecialCombos(gen.generatorType, gen.generatorValue);
227
+ if (combo !== undefined)
228
+ {
229
+ amount = combo.amt;
230
+ destination = combo.dest;
231
+ source = combo.source;
232
+ }
233
+ else if (destination === undefined)
234
+ {
235
+ SpessaSynthWarn(`Invalid generator type: ${gen.generatorType}`);
236
+ return undefined;
237
+ }
238
+ return new Articulator(
239
+ source,
240
+ 0,
241
+ destination,
242
+ amount,
243
+ 0
244
+ );
245
+ }
246
+
247
+
248
+ /**
249
+ * @param mod {Modulator}
250
+ * @returns {Articulator|undefined}
251
+ */
252
+ export function getDLSArticulatorFromSf2Modulator(mod)
253
+ {
254
+ if (mod.transformType !== 0)
255
+ {
256
+ SpessaSynthWarn("Other transform types are not supported.");
257
+ return undefined;
258
+ }
259
+ let source = getDLSSourceFromSf2Source(mod.sourceUsesCC, mod.sourceIndex);
260
+ let sourceCurve = mod.sourceCurveType;
261
+ let sourceBipolar = mod.sourcePolarity;
262
+ let sourceDirection = mod.sourceDirection;
263
+ if (source === undefined)
264
+ {
265
+ SpessaSynthWarn(`Invalid source: ${mod.sourceIndex}, CC: ${mod.sourceUsesCC}`);
266
+ return undefined;
267
+ }
268
+ let control = getDLSSourceFromSf2Source(mod.secSrcUsesCC, mod.secSrcIndex);
269
+ let controlCurve = mod.secSrcCurveType;
270
+ let controlBipolar = mod.secSrcPolarity;
271
+ let controlDirection = mod.secSrcDirection;
272
+ if (control === undefined)
273
+ {
274
+ SpessaSynthWarn(`Invalid secondary source: ${mod.secSrcIndex}, CC: ${mod.secSrcUsesCC}`);
275
+ return undefined;
276
+ }
277
+ let dlsDestinationFromSf2 = getDLSDestinationFromSf2(mod.modulatorDestination, mod.transformAmount);
278
+ let destination = dlsDestinationFromSf2;
279
+ let amt = mod.transformAmount;
280
+ if (dlsDestinationFromSf2?.dest !== undefined)
281
+ {
282
+ destination = dlsDestinationFromSf2.dest;
283
+ amt = dlsDestinationFromSf2.amount;
284
+ }
285
+ const specialCombo = checkSF2SpecialCombos(mod.modulatorDestination, mod.transformAmount);
286
+ if (specialCombo !== undefined)
287
+ {
288
+ amt = specialCombo.amt;
289
+ // move source to control
290
+ control = source;
291
+ controlCurve = sourceCurve;
292
+ controlBipolar = sourceBipolar;
293
+ controlDirection = sourceDirection;
294
+
295
+ // set source as static as it's either: env, lfo or keynum
296
+ sourceCurve = modulatorCurveTypes.linear;
297
+ sourceBipolar = specialCombo.isBipolar ? 1 : 0;
298
+ sourceDirection = 0;
299
+ source = specialCombo.source;
300
+ destination = specialCombo.dest;
301
+ }
302
+ else if (destination === undefined)
303
+ {
304
+ SpessaSynthWarn(`Invalid destination: ${mod.modulatorDestination}`);
305
+ return undefined;
306
+ }
307
+
308
+ // source curve type maps to desfont curve type in section 2.10, table 9
309
+ let transform = 0;
310
+ transform |= controlCurve << 4;
311
+ transform |= controlBipolar << 8;
312
+ transform |= controlDirection << 9;
313
+
314
+ transform |= sourceCurve << 10;
315
+ transform |= sourceBipolar << 14;
316
+ transform |= sourceDirection << 15;
317
+ return new Articulator(
318
+ source,
319
+ control,
320
+ destination,
321
+ amt,
322
+ transform
323
+ );
324
+ }
@@ -0,0 +1,101 @@
1
+ import { combineArrays, IndexedByteArray } from "../../../utils/indexed_array.js";
2
+ import { writeDword, writeWord } from "../../../utils/byte_functions/little_endian.js";
3
+ import { generatorTypes } from "../generator.js";
4
+ import { writeRIFFOddSize } from "../riff_chunk.js";
5
+ import { writeWavesample } from "./wsmp.js";
6
+ import { writeArticulator } from "./art2.js";
7
+
8
+ /**
9
+ * @param zone {BasicInstrumentZone}
10
+ * @this {BasicSoundFont}
11
+ * @returns {IndexedByteArray}
12
+ */
13
+ export function writeDLSRegion(zone)
14
+ {
15
+ // region header
16
+ const rgnhData = new IndexedByteArray(14);
17
+ // keyRange
18
+ writeWord(rgnhData, Math.max(zone.keyRange.min, 0));
19
+ writeWord(rgnhData, zone.keyRange.max);
20
+ // velRange
21
+ writeWord(rgnhData, Math.max(zone.velRange.min, 0));
22
+ writeWord(rgnhData, zone.velRange.max);
23
+ // fusOptions
24
+ writeWord(rgnhData, 0);
25
+ // keyGroup (exclusive class)
26
+ const exclusive = zone.getGeneratorValue(generatorTypes.exclusiveClass, 0);
27
+ writeWord(rgnhData, exclusive);
28
+ // usLayer
29
+ writeWord(rgnhData, 0);
30
+ const rgnh = writeRIFFOddSize(
31
+ "rgnh",
32
+ rgnhData
33
+ );
34
+
35
+ // wavesample (Wsmp)
36
+ const wsmp = writeWavesample(
37
+ zone.sample,
38
+ zone.getGeneratorValue(generatorTypes.overridingRootKey, zone.sample.samplePitch),
39
+ zone.getGeneratorValue(
40
+ generatorTypes.fineTune,
41
+ 0
42
+ ) + zone.getGeneratorValue(generatorTypes.coarseTune, 0) * 100
43
+ + zone.sample.samplePitchCorrection,
44
+ zone.getGeneratorValue(generatorTypes.initialAttenuation, 0),
45
+ // calculate loop with offsets
46
+ zone.sample.sampleLoopStartIndex
47
+ + zone.getGeneratorValue(generatorTypes.startloopAddrsOffset, 0)
48
+ + zone.getGeneratorValue(generatorTypes.startloopAddrsCoarseOffset, 0) * 32768,
49
+ zone.sample.sampleLoopEndIndex
50
+ + zone.getGeneratorValue(generatorTypes.endloopAddrsOffset, 0)
51
+ + zone.getGeneratorValue(generatorTypes.endloopAddrsCoarseOffset, 0) * 32768,
52
+ zone.getGeneratorValue(generatorTypes.sampleModes, 0)
53
+ );
54
+
55
+ // wavelink (wlnk)
56
+ const wlnkData = new IndexedByteArray(12);
57
+ writeWord(wlnkData, 0); // fusOptions
58
+ writeWord(wlnkData, 0); // usPhaseGroup
59
+ let sampleType = 0;
60
+ switch (zone.sample.sampleType)
61
+ {
62
+ default:
63
+ case 1:
64
+ case 4:
65
+ // mono/left
66
+ sampleType = 0;
67
+ break;
68
+
69
+ case 2:
70
+ // right
71
+ sampleType = 1;
72
+ }
73
+ writeDword(wlnkData, sampleType); // ulChannel
74
+ writeDword(wlnkData, this.samples.indexOf(zone.sample)); // ulTableIndex
75
+ const wlnk = writeRIFFOddSize(
76
+ "wlnk",
77
+ wlnkData
78
+ );
79
+
80
+ // art
81
+ const art2 = writeArticulator(zone);
82
+
83
+ const lar2 = writeRIFFOddSize(
84
+ "lar2",
85
+ art2,
86
+ false,
87
+ true
88
+ );
89
+
90
+ return writeRIFFOddSize(
91
+ "rgn2",
92
+ combineArrays([
93
+ rgnh,
94
+ wsmp,
95
+ wlnk,
96
+ lar2
97
+ ]),
98
+ false,
99
+ true
100
+ );
101
+ }
@@ -0,0 +1,74 @@
1
+ import { combineArrays, IndexedByteArray } from "../../../utils/indexed_array.js";
2
+ import { writeDword, writeWord } from "../../../utils/byte_functions/little_endian.js";
3
+ import { writeRIFFOddSize } from "../riff_chunk.js";
4
+ import { writeWavesample } from "./wsmp.js";
5
+ import { getStringBytes } from "../../../utils/byte_functions/string.js";
6
+ import { SpessaSynthInfo } from "../../../utils/loggin.js";
7
+ import { consoleColors } from "../../../utils/other.js";
8
+
9
+ /**
10
+ * @param sample {BasicSample}
11
+ * @returns {IndexedByteArray}
12
+ */
13
+ export function writeDLSSample(sample)
14
+ {
15
+ const fmtData = new IndexedByteArray(18);
16
+ writeWord(fmtData, 1); // wFormatTag
17
+ writeWord(fmtData, 1); // wChannels
18
+ writeDword(fmtData, sample.sampleRate);
19
+ writeDword(fmtData, sample.sampleRate * 2); // 16-bit samples
20
+ writeWord(fmtData, 2); // wBlockAlign
21
+ writeWord(fmtData, 16); // wBitsPerSample
22
+ const fmt = writeRIFFOddSize(
23
+ "fmt ",
24
+ fmtData
25
+ );
26
+ const wsmp = writeWavesample(
27
+ sample,
28
+ sample.samplePitch,
29
+ sample.samplePitchCorrection,
30
+ 0,
31
+ sample.sampleLoopStartIndex,
32
+ sample.sampleLoopEndIndex,
33
+ 1
34
+ );
35
+
36
+ const audio = sample.getAudioData();
37
+ const data16 = new Int16Array(audio.length);
38
+ for (let i = 0; i < audio.length; i++)
39
+ {
40
+ data16[i] = audio[i] * 32768;
41
+ }
42
+ const data = writeRIFFOddSize(
43
+ "data",
44
+ new IndexedByteArray(data16.buffer)
45
+ );
46
+
47
+ const inam = writeRIFFOddSize(
48
+ "INAM",
49
+ getStringBytes(sample.sampleName)
50
+ );
51
+ const info = writeRIFFOddSize(
52
+ "INFO",
53
+ inam,
54
+ false,
55
+ true
56
+ );
57
+ SpessaSynthInfo(
58
+ `%cSaved %c${sample.sampleName}%c succesfully!`,
59
+ consoleColors.recognized,
60
+ consoleColors.value,
61
+ consoleColors.recognized
62
+ );
63
+ return writeRIFFOddSize(
64
+ "wave",
65
+ combineArrays([
66
+ fmt,
67
+ wsmp,
68
+ data,
69
+ info
70
+ ]),
71
+ false,
72
+ true
73
+ );
74
+ }
@@ -0,0 +1,118 @@
1
+ import { writeRIFFOddSize } from "../riff_chunk.js";
2
+ import { writeDword } from "../../../utils/byte_functions/little_endian.js";
3
+ import { combineArrays, IndexedByteArray } from "../../../utils/indexed_array.js";
4
+ import { writeLins } from "./lins.js";
5
+ import { getStringBytes, writeStringAsBytes } from "../../../utils/byte_functions/string.js";
6
+ import { writeWavePool } from "./wvpl.js";
7
+ import { SpessaSynthGroupCollapsed, SpessaSynthGroupEnd, SpessaSynthInfo } from "../../../utils/loggin.js";
8
+ import { consoleColors } from "../../../utils/other.js";
9
+
10
+ /**
11
+ * Write the soundfont as a .dls file. Experimental
12
+ * @this {BasicSoundFont}
13
+ * @returns {Uint8Array}
14
+ */
15
+ export function writeDLS()
16
+ {
17
+ SpessaSynthGroupCollapsed(
18
+ "%cSaving DLS...",
19
+ consoleColors.info
20
+ );
21
+ // write colh
22
+ const colhNum = new IndexedByteArray(4);
23
+ writeDword(colhNum, this.presets.length);
24
+ const colh = writeRIFFOddSize(
25
+ "colh",
26
+ colhNum
27
+ );
28
+ SpessaSynthGroupCollapsed(
29
+ "%cWriting instruments...",
30
+ consoleColors.info
31
+ );
32
+ const lins = writeLins.apply(this);
33
+ SpessaSynthInfo(
34
+ "%cSuccess!",
35
+ consoleColors.recognized
36
+ );
37
+ SpessaSynthGroupEnd();
38
+
39
+ SpessaSynthGroupCollapsed(
40
+ "%cWriting WAVE samples...",
41
+ consoleColors.info
42
+ );
43
+ const wavepool = writeWavePool.apply(this);
44
+ const wvpl = wavepool.data;
45
+ const ptblOffsets = wavepool.indexes;
46
+ SpessaSynthInfo("%cSucceeded!", consoleColors.recognized);
47
+ SpessaSynthGroupEnd();
48
+
49
+ // write ptbl
50
+ const ptblData = new IndexedByteArray(8 + 8 * ptblOffsets.length);
51
+ writeDword(ptblData, 8);
52
+ writeDword(ptblData, ptblOffsets.length);
53
+ for (const offset of ptblOffsets)
54
+ {
55
+ writeDword(ptblData, offset);
56
+ }
57
+ const ptbl = writeRIFFOddSize(
58
+ "ptbl",
59
+ ptblData
60
+ );
61
+
62
+ this.soundFontInfo["ICMT"] = (this.soundFontInfo["ICMT"] || "Soundfont") + "\nConverted from SF2 to DLS using SpessaSynth";
63
+ this.soundFontInfo["ISFT"] = "SpessaSynth";
64
+ // write INFO
65
+ const infos = [];
66
+ for (const [info, data] of Object.entries(this.soundFontInfo))
67
+ {
68
+ if (
69
+ info !== "ICMT" &&
70
+ info !== "INAM" &&
71
+ info !== "ICRD" &&
72
+ info !== "IENG" &&
73
+ info !== "ICOP" &&
74
+ info !== "ISFT" &&
75
+ info !== "ISBJ"
76
+ )
77
+ {
78
+ continue;
79
+ }
80
+ infos.push(
81
+ writeRIFFOddSize(
82
+ info,
83
+ getStringBytes(data)
84
+ )
85
+ );
86
+ }
87
+ const info = writeRIFFOddSize(
88
+ "INFO",
89
+ combineArrays(infos),
90
+ false,
91
+ true
92
+ );
93
+
94
+ const out = new IndexedByteArray(
95
+ colh.length
96
+ + lins.length
97
+ + ptbl.length
98
+ + wvpl.length
99
+ + info.length
100
+ + 4);
101
+ writeStringAsBytes(out, "DLS ");
102
+ out.set(combineArrays([
103
+ colh,
104
+ lins,
105
+ ptbl,
106
+ wvpl,
107
+ info
108
+ ]), 4);
109
+ SpessaSynthInfo(
110
+ "%cSaved succesfully!",
111
+ consoleColors.recognized
112
+ );
113
+ SpessaSynthGroupEnd();
114
+ return writeRIFFOddSize(
115
+ "RIFF",
116
+ out
117
+ );
118
+ }
@@ -0,0 +1,74 @@
1
+ import { writeDword, writeWord } from "../../../utils/byte_functions/little_endian.js";
2
+ import { IndexedByteArray } from "../../../utils/indexed_array.js";
3
+ import { writeRIFFOddSize } from "../riff_chunk.js";
4
+
5
+ /**
6
+ * @param sample {BasicSample}
7
+ * @param rootKey {number}
8
+ * @param tuning {number}
9
+ * @param attenuationCentibels {number} CENTIBELS, NO CORRECTION
10
+ * @param loopStart {number}
11
+ * @param loopEnd {number}
12
+ * @param loopingMode {number}
13
+ * @returns {IndexedByteArray}
14
+ */
15
+ export function writeWavesample(
16
+ sample,
17
+ rootKey,
18
+ tuning,
19
+ attenuationCentibels,
20
+ loopStart,
21
+ loopEnd,
22
+ loopingMode)
23
+ {
24
+ // fixed size because always one loop
25
+ const wsmpData = new IndexedByteArray(36);
26
+ writeDword(wsmpData, 20); // cbSize
27
+ // usUnityNote (apply root pitch here)
28
+ writeWord(wsmpData, rootKey);
29
+ // sFineTune
30
+ writeWord(wsmpData, tuning);
31
+
32
+ // gain correction, use InitialAttenuation, apply attenuation correction
33
+ const attenuationCb = attenuationCentibels * 0.4;
34
+
35
+ // gain correction: Each unit of gain represents 1/655360 dB
36
+ const lGain = Math.floor(attenuationCb * -65536);
37
+ writeDword(wsmpData, lGain);
38
+ // fulOptions
39
+ writeDword(wsmpData, 0);
40
+
41
+ const loopSize = loopEnd - loopStart;
42
+ let loopCount = 1;
43
+ let ulLoopType = 0;
44
+ switch (loopingMode)
45
+ {
46
+ default:
47
+ case 0:
48
+ // no loop
49
+ loopCount = 0;
50
+ break;
51
+
52
+ case 1:
53
+ // loop
54
+ ulLoopType = 0;
55
+ loopCount = 1;
56
+ break;
57
+
58
+ case 3:
59
+ // loop and release
60
+ ulLoopType = 1;
61
+ loopCount = 1;
62
+ }
63
+
64
+ // cSampleLoops
65
+ writeDword(wsmpData, loopCount);
66
+ writeDword(wsmpData, 16); // cbSize
67
+ writeDword(wsmpData, ulLoopType);
68
+ writeDword(wsmpData, loopStart);
69
+ writeDword(wsmpData, loopSize);
70
+ return writeRIFFOddSize(
71
+ "wsmp",
72
+ wsmpData
73
+ );
74
+ }