spessasynth_core 3.27.8 → 4.0.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 (157) hide show
  1. package/README.md +85 -51
  2. package/dist/index.d.ts +4057 -0
  3. package/dist/index.js +17188 -0
  4. package/dist/index.js.map +1 -0
  5. package/package.json +23 -6
  6. package/index.js +0 -132
  7. package/src/externals/README.md +0 -6
  8. package/src/externals/fflate/LICENSE +0 -21
  9. package/src/externals/fflate/fflate.min.js +0 -1
  10. package/src/externals/stbvorbis_sync/@types/stbvorbis_sync.d.ts +0 -12
  11. package/src/externals/stbvorbis_sync/LICENSE +0 -202
  12. package/src/externals/stbvorbis_sync/NOTICE +0 -6
  13. package/src/externals/stbvorbis_sync/stbvorbis_sync.min.js +0 -1
  14. package/src/midi/README.md +0 -32
  15. package/src/midi/basic_midi.js +0 -587
  16. package/src/midi/midi_builder.js +0 -203
  17. package/src/midi/midi_loader.js +0 -321
  18. package/src/midi/midi_message.js +0 -254
  19. package/src/midi/midi_sequence.js +0 -230
  20. package/src/midi/midi_tools/get_note_times.js +0 -154
  21. package/src/midi/midi_tools/midi_editor.js +0 -611
  22. package/src/midi/midi_tools/midi_writer.js +0 -105
  23. package/src/midi/midi_tools/rmidi_writer.js +0 -566
  24. package/src/midi/midi_tools/used_keys_loaded.js +0 -256
  25. package/src/midi/xmf_loader.js +0 -454
  26. package/src/sequencer/README.md +0 -9
  27. package/src/sequencer/events.js +0 -81
  28. package/src/sequencer/play.js +0 -362
  29. package/src/sequencer/process_event.js +0 -165
  30. package/src/sequencer/process_tick.js +0 -104
  31. package/src/sequencer/sequencer_engine.js +0 -372
  32. package/src/sequencer/song_control.js +0 -196
  33. package/src/soundfont/README.md +0 -11
  34. package/src/soundfont/basic_soundfont/basic_global_zone.js +0 -6
  35. package/src/soundfont/basic_soundfont/basic_instrument.js +0 -115
  36. package/src/soundfont/basic_soundfont/basic_instrument_zone.js +0 -45
  37. package/src/soundfont/basic_soundfont/basic_preset.js +0 -313
  38. package/src/soundfont/basic_soundfont/basic_preset_zone.js +0 -39
  39. package/src/soundfont/basic_soundfont/basic_sample.js +0 -477
  40. package/src/soundfont/basic_soundfont/basic_soundbank.js +0 -740
  41. package/src/soundfont/basic_soundfont/basic_zone.js +0 -145
  42. package/src/soundfont/basic_soundfont/generator.js +0 -76
  43. package/src/soundfont/basic_soundfont/generator_types.js +0 -151
  44. package/src/soundfont/basic_soundfont/modulator.js +0 -581
  45. package/src/soundfont/basic_soundfont/riff_chunk.js +0 -195
  46. package/src/soundfont/basic_soundfont/write_dls/art2.js +0 -174
  47. package/src/soundfont/basic_soundfont/write_dls/articulator.js +0 -49
  48. package/src/soundfont/basic_soundfont/write_dls/combine_zones.js +0 -374
  49. package/src/soundfont/basic_soundfont/write_dls/ins.js +0 -85
  50. package/src/soundfont/basic_soundfont/write_dls/lins.js +0 -15
  51. package/src/soundfont/basic_soundfont/write_dls/modulator_converter.js +0 -330
  52. package/src/soundfont/basic_soundfont/write_dls/rgn2.js +0 -120
  53. package/src/soundfont/basic_soundfont/write_dls/wave.js +0 -71
  54. package/src/soundfont/basic_soundfont/write_dls/write_dls.js +0 -124
  55. package/src/soundfont/basic_soundfont/write_dls/wsmp.js +0 -78
  56. package/src/soundfont/basic_soundfont/write_dls/wvpl.js +0 -35
  57. package/src/soundfont/basic_soundfont/write_sf2/ibag.js +0 -60
  58. package/src/soundfont/basic_soundfont/write_sf2/igen.js +0 -91
  59. package/src/soundfont/basic_soundfont/write_sf2/imod.js +0 -62
  60. package/src/soundfont/basic_soundfont/write_sf2/inst.js +0 -42
  61. package/src/soundfont/basic_soundfont/write_sf2/pbag.js +0 -57
  62. package/src/soundfont/basic_soundfont/write_sf2/pgen.js +0 -92
  63. package/src/soundfont/basic_soundfont/write_sf2/phdr.js +0 -61
  64. package/src/soundfont/basic_soundfont/write_sf2/pmod.js +0 -62
  65. package/src/soundfont/basic_soundfont/write_sf2/sdta.js +0 -131
  66. package/src/soundfont/basic_soundfont/write_sf2/shdr.js +0 -77
  67. package/src/soundfont/basic_soundfont/write_sf2/write.js +0 -287
  68. package/src/soundfont/dls/articulator_converter.js +0 -402
  69. package/src/soundfont/dls/dls_destinations.js +0 -38
  70. package/src/soundfont/dls/dls_instrument.js +0 -20
  71. package/src/soundfont/dls/dls_preset.js +0 -43
  72. package/src/soundfont/dls/dls_sample.js +0 -238
  73. package/src/soundfont/dls/dls_soundfont.js +0 -183
  74. package/src/soundfont/dls/dls_sources.js +0 -63
  75. package/src/soundfont/dls/dls_zone.js +0 -89
  76. package/src/soundfont/dls/read_articulation.js +0 -300
  77. package/src/soundfont/dls/read_instrument.js +0 -118
  78. package/src/soundfont/dls/read_instrument_list.js +0 -17
  79. package/src/soundfont/dls/read_lart.js +0 -35
  80. package/src/soundfont/dls/read_region.js +0 -157
  81. package/src/soundfont/dls/read_samples.js +0 -154
  82. package/src/soundfont/load_soundfont.js +0 -21
  83. package/src/soundfont/read_sf2/generators.js +0 -43
  84. package/src/soundfont/read_sf2/instrument_zones.js +0 -75
  85. package/src/soundfont/read_sf2/instruments.js +0 -71
  86. package/src/soundfont/read_sf2/modulators.js +0 -25
  87. package/src/soundfont/read_sf2/preset_zones.js +0 -79
  88. package/src/soundfont/read_sf2/presets.js +0 -80
  89. package/src/soundfont/read_sf2/samples.js +0 -317
  90. package/src/soundfont/read_sf2/soundfont.js +0 -452
  91. package/src/soundfont/read_sf2/zones.js +0 -28
  92. package/src/synthetizer/README.md +0 -7
  93. package/src/synthetizer/audio_engine/README.md +0 -9
  94. package/src/synthetizer/audio_engine/engine_components/compute_modulator.js +0 -289
  95. package/src/synthetizer/audio_engine/engine_components/controller_tables.js +0 -90
  96. package/src/synthetizer/audio_engine/engine_components/dynamic_modulator_system.js +0 -95
  97. package/src/synthetizer/audio_engine/engine_components/enums.js +0 -18
  98. package/src/synthetizer/audio_engine/engine_components/key_modifier_manager.js +0 -151
  99. package/src/synthetizer/audio_engine/engine_components/lfo.js +0 -26
  100. package/src/synthetizer/audio_engine/engine_components/lowpass_filter.js +0 -282
  101. package/src/synthetizer/audio_engine/engine_components/midi_audio_channel.js +0 -551
  102. package/src/synthetizer/audio_engine/engine_components/modulation_envelope.js +0 -181
  103. package/src/synthetizer/audio_engine/engine_components/modulator_curves.js +0 -89
  104. package/src/synthetizer/audio_engine/engine_components/soundfont_manager.js +0 -265
  105. package/src/synthetizer/audio_engine/engine_components/stereo_panner.js +0 -124
  106. package/src/synthetizer/audio_engine/engine_components/unit_converter.js +0 -73
  107. package/src/synthetizer/audio_engine/engine_components/voice.js +0 -525
  108. package/src/synthetizer/audio_engine/engine_components/volume_envelope.js +0 -402
  109. package/src/synthetizer/audio_engine/engine_components/wavetable_oscillator.js +0 -274
  110. package/src/synthetizer/audio_engine/engine_methods/controller_control/controller_change.js +0 -159
  111. package/src/synthetizer/audio_engine/engine_methods/controller_control/master_parameters.js +0 -53
  112. package/src/synthetizer/audio_engine/engine_methods/controller_control/reset_controllers.js +0 -254
  113. package/src/synthetizer/audio_engine/engine_methods/create_midi_channel.js +0 -20
  114. package/src/synthetizer/audio_engine/engine_methods/data_entry/awe32.js +0 -198
  115. package/src/synthetizer/audio_engine/engine_methods/data_entry/data_entry_coarse.js +0 -281
  116. package/src/synthetizer/audio_engine/engine_methods/data_entry/data_entry_fine.js +0 -109
  117. package/src/synthetizer/audio_engine/engine_methods/mute_channel.js +0 -17
  118. package/src/synthetizer/audio_engine/engine_methods/note_on.js +0 -220
  119. package/src/synthetizer/audio_engine/engine_methods/portamento_time.js +0 -92
  120. package/src/synthetizer/audio_engine/engine_methods/program_change.js +0 -35
  121. package/src/synthetizer/audio_engine/engine_methods/render_voice.js +0 -214
  122. package/src/synthetizer/audio_engine/engine_methods/soundfont_management/embedded_sound_bank.js +0 -42
  123. package/src/synthetizer/audio_engine/engine_methods/soundfont_management/get_preset.js +0 -0
  124. package/src/synthetizer/audio_engine/engine_methods/soundfont_management/update_preset_list.js +0 -19
  125. package/src/synthetizer/audio_engine/engine_methods/stopping_notes/kill_note.js +0 -23
  126. package/src/synthetizer/audio_engine/engine_methods/stopping_notes/note_off.js +0 -56
  127. package/src/synthetizer/audio_engine/engine_methods/stopping_notes/stop_all_channels.js +0 -16
  128. package/src/synthetizer/audio_engine/engine_methods/stopping_notes/stop_all_notes.js +0 -30
  129. package/src/synthetizer/audio_engine/engine_methods/stopping_notes/voice_killing.js +0 -63
  130. package/src/synthetizer/audio_engine/engine_methods/system_exclusive.js +0 -1058
  131. package/src/synthetizer/audio_engine/engine_methods/tuning_control/channel_pressure.js +0 -23
  132. package/src/synthetizer/audio_engine/engine_methods/tuning_control/pitch_wheel.js +0 -31
  133. package/src/synthetizer/audio_engine/engine_methods/tuning_control/poly_pressure.js +0 -29
  134. package/src/synthetizer/audio_engine/engine_methods/tuning_control/set_master_tuning.js +0 -15
  135. package/src/synthetizer/audio_engine/engine_methods/tuning_control/set_modulation_depth.js +0 -26
  136. package/src/synthetizer/audio_engine/engine_methods/tuning_control/set_octave_tuning.js +0 -19
  137. package/src/synthetizer/audio_engine/engine_methods/tuning_control/set_tuning.js +0 -27
  138. package/src/synthetizer/audio_engine/engine_methods/tuning_control/transpose_all_channels.js +0 -15
  139. package/src/synthetizer/audio_engine/engine_methods/tuning_control/transpose_channel.js +0 -34
  140. package/src/synthetizer/audio_engine/main_processor.js +0 -817
  141. package/src/synthetizer/audio_engine/snapshot/apply_synthesizer_snapshot.js +0 -16
  142. package/src/synthetizer/audio_engine/snapshot/channel_snapshot.js +0 -175
  143. package/src/synthetizer/audio_engine/snapshot/synthesizer_snapshot.js +0 -116
  144. package/src/synthetizer/audio_engine/synth_processor_options.js +0 -18
  145. package/src/synthetizer/synth_constants.js +0 -26
  146. package/src/utils/README.md +0 -8
  147. package/src/utils/buffer_to_wav.js +0 -197
  148. package/src/utils/byte_functions/big_endian.js +0 -32
  149. package/src/utils/byte_functions/little_endian.js +0 -77
  150. package/src/utils/byte_functions/string.js +0 -92
  151. package/src/utils/byte_functions/variable_length_quantity.js +0 -42
  152. package/src/utils/fill_with_defaults.js +0 -21
  153. package/src/utils/indexed_array.js +0 -34
  154. package/src/utils/loggin.js +0 -71
  155. package/src/utils/other.js +0 -92
  156. package/src/utils/sysex_detector.js +0 -58
  157. package/src/utils/xg_hacks.js +0 -193
@@ -1,740 +0,0 @@
1
- import {
2
- SpessaSynthGroup,
3
- SpessaSynthGroupCollapsed,
4
- SpessaSynthGroupEnd,
5
- SpessaSynthInfo,
6
- SpessaSynthWarn
7
- } from "../../utils/loggin.js";
8
- import { consoleColors } from "../../utils/other.js";
9
- import { write } from "./write_sf2/write.js";
10
- import { defaultModulators, Modulator } from "./modulator.js";
11
- import { writeDLS } from "./write_dls/write_dls.js";
12
- import { BasicSample, sampleTypes } from "./basic_sample.js";
13
- import { Generator } from "./generator.js";
14
- import { BasicInstrument } from "./basic_instrument.js";
15
- import { BasicPreset } from "./basic_preset.js";
16
- import { isXGDrums } from "../../utils/xg_hacks.js";
17
- import { generatorTypes } from "./generator_types.js";
18
- import { BasicGlobalZone } from "./basic_global_zone.js";
19
- import { stbvorbis } from "../../externals/stbvorbis_sync/stbvorbis_sync.min.js";
20
-
21
- /**
22
- * Represents a single sound bank, be it DLS or SF2.
23
- */
24
- class BasicSoundBank
25
- {
26
- /**
27
- * Indicates if the SF3/SF2Pack decoder is ready.
28
- * @type {Promise<boolean>}
29
- * @static
30
- */
31
- static isSF3DecoderReady = stbvorbis.isInitialized;
32
-
33
- /**
34
- * Soundfont's info stored as name: value. ifil and iver are stored as string representation of float (e.g., 2.1)
35
- * @type {Record<string, string|IndexedByteArray>}
36
- */
37
- soundFontInfo = {};
38
-
39
- /**
40
- * The soundfont's presets
41
- * @type {BasicPreset[]}
42
- * @readonly
43
- */
44
- presets = [];
45
-
46
- /**
47
- * The soundfont's samples
48
- * @type {BasicSample[]}
49
- * @readonly
50
- */
51
- samples = [];
52
-
53
- /**
54
- * The soundfont's instruments
55
- * @type {BasicInstrument[]}
56
- * @readonly
57
- */
58
- instruments = [];
59
-
60
- /**
61
- * Soundfont's default modulatorss
62
- * @type {Modulator[]}
63
- */
64
- defaultModulators = defaultModulators.map(m => Modulator.copy(m));
65
-
66
- /**
67
- * If the bank has custom default modulators (DMOD).
68
- * @type {boolean}
69
- */
70
- customDefaultModulators = false;
71
-
72
- /**
73
- * Checks for XG drumsets and considers if this soundfont is XG.
74
- * @type {boolean}
75
- */
76
- isXGBank = false;
77
-
78
- /**
79
- * Creates a new basic soundfont template (or copies)
80
- * @param data {undefined|{presets: BasicPreset[], info: Record<string, string>}}
81
- */
82
- constructor(data = undefined)
83
- {
84
- if (data?.presets)
85
- {
86
- this.soundFontInfo = data.info;
87
- this.addPresets(...data.presets);
88
- /**
89
- * @type {BasicInstrument[]}
90
- */
91
- const instrumentList = [];
92
- for (const preset of data.presets)
93
- {
94
- for (const zone of preset.presetZones)
95
- {
96
- if (!instrumentList.includes(zone.instrument))
97
- {
98
- instrumentList.push(zone.instrument);
99
- }
100
- }
101
- }
102
- this.addInstruments(...instrumentList);
103
-
104
- /**
105
- * @type {BasicSample[]}
106
- */
107
- const sampleList = [];
108
-
109
- for (const instrument of instrumentList)
110
- {
111
- for (const zone of instrument.instrumentZones)
112
- {
113
- if (!sampleList.includes(zone.sample))
114
- {
115
- sampleList.push(zone.sample);
116
- }
117
- }
118
- }
119
- this.addSamples(...sampleList);
120
- }
121
- }
122
-
123
- /**
124
- * Merges soundfonts with the given order. Keep in mind that the info read is copied from the first one
125
- * @param soundfonts {...BasicSoundBank} the soundfonts to merge, the first overwrites the last
126
- * @returns {BasicSoundBank}
127
- */
128
- static mergeSoundBanks(...soundfonts)
129
- {
130
- const mainSf = soundfonts.shift();
131
- const presets = mainSf.presets;
132
- while (soundfonts.length)
133
- {
134
- const newPresets = soundfonts.shift().presets;
135
- newPresets.forEach(newPreset =>
136
- {
137
- if (
138
- presets.find(existingPreset => existingPreset.bank === newPreset.bank && existingPreset.program === newPreset.program) === undefined
139
- )
140
- {
141
- presets.push(newPreset);
142
- }
143
- });
144
- }
145
-
146
- return new BasicSoundBank({ presets: presets, info: mainSf.soundFontInfo });
147
- }
148
-
149
- /**
150
- * Creates a simple soundfont with one saw wave preset.
151
- * @returns {Promise<ArrayBufferLike>}
152
- */
153
- static async getDummySoundfontFile()
154
- {
155
- const font = new BasicSoundBank();
156
- const sample = new BasicSample(
157
- "Saw",
158
- 44100,
159
- 65,
160
- 20,
161
- sampleTypes.monoSample,
162
- 0,
163
- 127
164
- );
165
- const sampleData = new Float32Array(128);
166
- for (let i = 0; i < 128; i++)
167
- {
168
- sampleData[i] = (i / 128) * 2 - 1;
169
- }
170
- sample.setAudioData(sampleData);
171
- font.addSamples(sample);
172
-
173
- const gZone = new BasicGlobalZone();
174
- gZone.addGenerators(
175
- new Generator(generatorTypes.initialAttenuation, 375),
176
- new Generator(generatorTypes.releaseVolEnv, -1000),
177
- new Generator(generatorTypes.sampleModes, 1)
178
- );
179
-
180
- const inst = new BasicInstrument();
181
- inst.instrumentName = "Saw Wave";
182
- inst.globalZone = gZone;
183
-
184
- const zone1 = inst.createZone();
185
- zone1.setSample(sample);
186
-
187
- const zone2 = inst.createZone();
188
- zone2.setSample(sample);
189
- zone2.addGenerators(new Generator(generatorTypes.fineTune, -9));
190
-
191
- font.addInstruments(inst);
192
-
193
-
194
- const preset = new BasicPreset(font);
195
- preset.presetName = "Saw Wave";
196
- const pZone = preset.createZone();
197
- pZone.setInstrument(inst);
198
-
199
- font.addPresets(preset);
200
-
201
- font.soundFontInfo["ifil"] = "2.1";
202
- font.soundFontInfo["isng"] = "E-mu 10K2";
203
- font.soundFontInfo["INAM"] = "Dummy";
204
- font.flush();
205
- const f = await font.write();
206
- return f.buffer;
207
- }
208
-
209
- /**
210
- * @param presets {BasicPreset}
211
- */
212
- addPresets(...presets)
213
- {
214
- this.presets.push(...presets);
215
- }
216
-
217
- /**
218
- * @param instruments {BasicInstrument}
219
- */
220
- addInstruments(...instruments)
221
- {
222
- this.instruments.push(...instruments);
223
- }
224
-
225
- /**
226
- * @param samples {BasicSample}
227
- */
228
- addSamples(...samples)
229
- {
230
- this.samples.push(...samples);
231
- }
232
-
233
- /**
234
- * Clones samples into this bank
235
- * @param sample {BasicSample} samples to copy
236
- * @returns {BasicSample} copied sample, if a sample exists with that name, it is returned instead
237
- */
238
- cloneSample(sample)
239
- {
240
- const duplicate = this.samples.find(s => s.sampleName === sample.sampleName);
241
- if (duplicate)
242
- {
243
- return duplicate;
244
- }
245
- const newSample = new BasicSample(
246
- sample.sampleName,
247
- sample.sampleRate,
248
- sample.samplePitch,
249
- sample.samplePitchCorrection,
250
- sample.sampleType,
251
- sample.sampleLoopStartIndex,
252
- sample.sampleLoopEndIndex
253
- );
254
- if (sample.isCompressed)
255
- {
256
- newSample.setCompressedData(sample.compressedData.slice());
257
- }
258
- else
259
- {
260
- newSample.setAudioData(sample.getAudioData());
261
- }
262
- this.addSamples(newSample);
263
- if (sample.linkedSample)
264
- {
265
- const clonedLinked = this.cloneSample(sample.linkedSample);
266
- // sanity check
267
- if (!clonedLinked.linkedSample)
268
- {
269
- newSample.setLinkedSample(clonedLinked, newSample.sampleType);
270
- }
271
- }
272
- return newSample;
273
- }
274
-
275
-
276
- /**
277
- * Clones an instruments into this bank
278
- * @param instrument {BasicInstrument}
279
- * @returns {BasicInstrument} the copied instrument, if an instrument exists with that name, it is returned instead
280
- */
281
- cloneInstrument(instrument)
282
- {
283
- const duplicate = this.instruments.find(i => i.instrumentName === instrument.instrumentName);
284
- if (duplicate)
285
- {
286
- return duplicate;
287
- }
288
- const newInstrument = new BasicInstrument();
289
- newInstrument.instrumentName = instrument.instrumentName;
290
- newInstrument.globalZone.copyFrom(instrument.globalZone);
291
- for (const zone of instrument.instrumentZones)
292
- {
293
- const copiedZone = newInstrument.createZone();
294
- copiedZone.copyFrom(zone);
295
- copiedZone.setSample(this.cloneSample(zone.sample));
296
- }
297
- this.addInstruments(newInstrument);
298
- return newInstrument;
299
- }
300
-
301
- // noinspection JSUnusedGlobalSymbols
302
- /**
303
- * Clones presets into this sound bank
304
- * @param preset {BasicPreset}
305
- * @returns {BasicPreset} the copied preset, if a preset exists with that name, it is returned instead
306
- */
307
- clonePreset(preset)
308
- {
309
- const duplicate = this.presets.find(p => p.presetName === preset.presetName);
310
- if (duplicate)
311
- {
312
- return duplicate;
313
- }
314
- const newPreset = new BasicPreset(this);
315
- newPreset.presetName = preset.presetName;
316
- newPreset.bank = preset.bank;
317
- newPreset.program = preset.program;
318
- newPreset.library = preset.library;
319
- newPreset.genre = preset.genre;
320
- newPreset.morphology = preset.morphology;
321
- newPreset.globalZone.copyFrom(preset.globalZone);
322
- for (const zone of preset.presetZones)
323
- {
324
- const copiedZone = newPreset.createZone();
325
- copiedZone.copyFrom(zone);
326
- copiedZone.setInstrument(this.cloneInstrument(zone.instrument));
327
- }
328
-
329
- this.addPresets(newPreset);
330
- return newPreset;
331
- }
332
-
333
- flush()
334
- {
335
- this.presets.sort((a, b) =>
336
- {
337
- if (a.bank !== b.bank)
338
- {
339
- return a.bank - b.bank;
340
- }
341
- return a.program - b.program;
342
- });
343
- this._parseInternal();
344
- }
345
-
346
- /**
347
- * parses the bank after loading is done
348
- * @protected
349
- */
350
- _parseInternal()
351
- {
352
- this.isXGBank = false;
353
- // definitions for XG:
354
- // at least one preset with bank 127, 126 or 120
355
- // MUST be a valid XG bank.
356
- // allowed banks: (see XG specification)
357
- // 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 16, 17, 24,
358
- // 25, 27, 28, 29, 30, 31, 32, 33, 40, 41, 48, 56, 57, 58,
359
- // 64, 65, 66, 126, 127
360
- const allowedPrograms = new Set([
361
- 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 16, 17, 24,
362
- 25, 27, 28, 29, 30, 31, 32, 33, 40, 41, 48, 56, 57, 58,
363
- 64, 65, 66, 126, 127
364
- ]);
365
- for (const preset of this.presets)
366
- {
367
- if (isXGDrums(preset.bank))
368
- {
369
- this.isXGBank = true;
370
- if (!allowedPrograms.has(preset.program))
371
- {
372
- // not valid!
373
- this.isXGBank = false;
374
- SpessaSynthInfo(
375
- `%cThis bank is not valid XG. Preset %c${preset.bank}:${preset.program}%c is not a valid XG drum. XG mode will use presets on bank 128.`,
376
- consoleColors.info,
377
- consoleColors.value,
378
- consoleColors.info
379
- );
380
- break;
381
- }
382
- }
383
- }
384
- }
385
-
386
- /**
387
- * Trims a sound bank to only contain samples in a given MIDI file
388
- * @param mid {BasicMIDI} - the MIDI file
389
- */
390
- trimSoundBank(mid)
391
- {
392
- const soundfont = this;
393
-
394
- /**
395
- * @param instrument {BasicInstrument}
396
- * @param combos {{key: number, velocity: number}[]}
397
- * @returns {number}
398
- */
399
- function trimInstrumentZones(instrument, combos)
400
- {
401
- let trimmedIZones = 0;
402
- for (let iZoneIndex = 0; iZoneIndex < instrument.instrumentZones.length; iZoneIndex++)
403
- {
404
- const iZone = instrument.instrumentZones[iZoneIndex];
405
- const iKeyRange = iZone.keyRange;
406
- const iVelRange = iZone.velRange;
407
- let isIZoneUsed = false;
408
- for (const iCombo of combos)
409
- {
410
- if (
411
- (iCombo.key >= iKeyRange.min && iCombo.key <= iKeyRange.max) &&
412
- (iCombo.velocity >= iVelRange.min && iCombo.velocity <= iVelRange.max)
413
- )
414
- {
415
- isIZoneUsed = true;
416
- break;
417
- }
418
- }
419
- if (!isIZoneUsed)
420
- {
421
- SpessaSynthInfo(
422
- `%c${iZone.sample.sampleName} %cremoved from %c${instrument.instrumentName}%c.`,
423
- consoleColors.recognized,
424
- consoleColors.info,
425
- consoleColors.recognized,
426
- consoleColors.info
427
- );
428
- if (instrument.deleteZone(iZoneIndex))
429
- {
430
- trimmedIZones++;
431
- iZoneIndex--;
432
- SpessaSynthInfo(
433
- `%c${iZone.sample.sampleName} %cdeleted`,
434
- consoleColors.recognized,
435
- consoleColors.info
436
- );
437
- }
438
- if (iZone.sample.useCount < 1)
439
- {
440
- soundfont.deleteSample(iZone.sample);
441
- }
442
- }
443
-
444
- }
445
- return trimmedIZones;
446
- }
447
-
448
- SpessaSynthGroup(
449
- "%cTrimming soundfont...",
450
- consoleColors.info
451
- );
452
- const usedProgramsAndKeys = mid.getUsedProgramsAndKeys(soundfont);
453
-
454
- SpessaSynthGroupCollapsed(
455
- "%cModifying soundfont...",
456
- consoleColors.info
457
- );
458
- SpessaSynthInfo("Detected keys for midi:", usedProgramsAndKeys);
459
- // modify the soundfont to only include programs and samples that are used
460
- for (let presetIndex = 0; presetIndex < soundfont.presets.length; presetIndex++)
461
- {
462
- const p = soundfont.presets[presetIndex];
463
- const string = p.bank + ":" + p.program;
464
- const used = usedProgramsAndKeys[string];
465
- if (used === undefined)
466
- {
467
- SpessaSynthInfo(
468
- `%cDeleting preset %c${p.presetName}%c and its zones`,
469
- consoleColors.info,
470
- consoleColors.recognized,
471
- consoleColors.info
472
- );
473
- soundfont.deletePreset(p);
474
- presetIndex--;
475
- }
476
- else
477
- {
478
- const combos = [...used].map(s =>
479
- {
480
- const split = s.split("-");
481
- return {
482
- key: parseInt(split[0]),
483
- velocity: parseInt(split[1])
484
- };
485
- });
486
- SpessaSynthGroupCollapsed(
487
- `%cTrimming %c${p.presetName}`,
488
- consoleColors.info,
489
- consoleColors.recognized
490
- );
491
- SpessaSynthInfo(`Keys for ${p.presetName}:`, combos);
492
- let trimmedZones = 0;
493
- // clean the preset to only use zones that are used
494
- for (let zoneIndex = 0; zoneIndex < p.presetZones.length; zoneIndex++)
495
- {
496
- const zone = p.presetZones[zoneIndex];
497
- const keyRange = zone.keyRange;
498
- const velRange = zone.velRange;
499
- // check if any of the combos matches the zone
500
- let isZoneUsed = false;
501
- for (const combo of combos)
502
- {
503
- if (
504
- (combo.key >= keyRange.min && combo.key <= keyRange.max) &&
505
- (combo.velocity >= velRange.min && combo.velocity <= velRange.max)
506
- )
507
- {
508
- // zone is used, trim the instrument zones
509
- isZoneUsed = true;
510
- const trimmedIZones = trimInstrumentZones(zone.instrument, combos);
511
- SpessaSynthInfo(
512
- `%cTrimmed off %c${trimmedIZones}%c zones from %c${zone.instrument.instrumentName}`,
513
- consoleColors.info,
514
- consoleColors.recognized,
515
- consoleColors.info,
516
- consoleColors.recognized
517
- );
518
- break;
519
- }
520
- }
521
- if (!isZoneUsed)
522
- {
523
- trimmedZones++;
524
- p.deleteZone(zoneIndex);
525
- if (zone.instrument.useCount < 1)
526
- {
527
- soundfont.deleteInstrument(zone.instrument);
528
- }
529
- zoneIndex--;
530
- }
531
- }
532
- SpessaSynthInfo(
533
- `%cTrimmed off %c${trimmedZones}%c zones from %c${p.presetName}`,
534
- consoleColors.info,
535
- consoleColors.recognized,
536
- consoleColors.info,
537
- consoleColors.recognized
538
- );
539
- SpessaSynthGroupEnd();
540
- }
541
- }
542
- soundfont.removeUnusedElements();
543
-
544
- soundfont.soundFontInfo["ICMT"] = `NOTE: This soundfont was trimmed by SpessaSynth to only contain presets used in "${mid.midiName}"\n\n`
545
- + soundfont.soundFontInfo["ICMT"];
546
-
547
- SpessaSynthInfo(
548
- "%cSoundfont modified!",
549
- consoleColors.recognized
550
- );
551
- SpessaSynthGroupEnd();
552
- SpessaSynthGroupEnd();
553
- }
554
-
555
- removeUnusedElements()
556
- {
557
- this.instruments = this.instruments.filter(i =>
558
- {
559
- i.deleteUnusedZones();
560
- const deletable = i.useCount < 1;
561
- if (deletable)
562
- {
563
- i.deleteInstrument();
564
- }
565
- return !deletable;
566
- });
567
- this.samples = this.samples.filter(s =>
568
- {
569
- const deletable = s.useCount < 1;
570
- if (deletable)
571
- {
572
- s.deleteSample();
573
- }
574
- return !deletable;
575
- });
576
- }
577
-
578
- /**
579
- * @param instrument {BasicInstrument}
580
- */
581
- deleteInstrument(instrument)
582
- {
583
- instrument.deleteInstrument();
584
- this.instruments.splice(this.instruments.indexOf(instrument), 1);
585
- }
586
-
587
- /**
588
- * @param preset {BasicPreset}
589
- */
590
- deletePreset(preset)
591
- {
592
- preset.deletePreset();
593
- this.presets.splice(this.presets.indexOf(preset), 1);
594
- }
595
-
596
- /**
597
- * @param sample {BasicSample}
598
- */
599
- deleteSample(sample)
600
- {
601
- sample.deleteSample();
602
- this.samples.splice(this.samples.indexOf(sample), 1);
603
- }
604
-
605
- /**
606
- * Get the appropriate preset, undefined if not found
607
- * @param bankNr {number}
608
- * @param programNr {number}
609
- * @param allowXGDrums {boolean} if true, allows XG drum banks (120, 126 and 127) as drum preset
610
- * @return {BasicPreset}
611
- */
612
- getPresetNoFallback(bankNr, programNr, allowXGDrums = false)
613
- {
614
- const isDrum = bankNr === 128 || (allowXGDrums && isXGDrums(bankNr));
615
- // check for exact match
616
- let p;
617
- if (isDrum)
618
- {
619
- p = this.presets.find(p => p.bank === bankNr && p.isDrumPreset(allowXGDrums) && p.program === programNr);
620
- }
621
- else
622
- {
623
- p = this.presets.find(p => p.bank === bankNr && p.program === programNr);
624
- }
625
- if (p)
626
- {
627
- return p;
628
- }
629
- // no match...
630
- if (isDrum)
631
- {
632
- if (allowXGDrums)
633
- {
634
- // try any drum preset with matching program?
635
- const p = this.presets.find(p => p.isDrumPreset(allowXGDrums) && p.program === programNr);
636
- if (p)
637
- {
638
- return p;
639
- }
640
- }
641
- }
642
- return undefined;
643
- }
644
-
645
- /**
646
- * Get the appropriate preset
647
- * @param bankNr {number}
648
- * @param programNr {number}
649
- * @param allowXGDrums {boolean} if true, allows XG drum banks (120, 126 and 127) as drum preset
650
- * @returns {BasicPreset}
651
- */
652
- getPreset(bankNr, programNr, allowXGDrums = false)
653
- {
654
- const isDrums = bankNr === 128 || (allowXGDrums && isXGDrums(bankNr));
655
- // check for exact match
656
- let preset;
657
- // only allow drums if the preset is considered to be a drum preset
658
- if (isDrums)
659
- {
660
- preset = this.presets.find(p => p.bank === bankNr && p.isDrumPreset(allowXGDrums) && p.program === programNr);
661
- }
662
- else
663
- {
664
- preset = this.presets.find(p => p.bank === bankNr && p.program === programNr);
665
- }
666
- if (preset)
667
- {
668
- return preset;
669
- }
670
- // no match...
671
- if (isDrums)
672
- {
673
- // drum preset: find any preset with bank 128
674
- preset = this.presets.find(p => p.isDrumPreset(allowXGDrums) && p.program === programNr);
675
- if (!preset)
676
- {
677
- // only allow 128, otherwise it would default to XG SFX
678
- preset = this.presets.find(p => p.isDrumPreset(allowXGDrums));
679
- }
680
- }
681
- else
682
- {
683
- // non-drum preset: find any preset with the given program that is not a drum preset
684
- preset = this.presets.find(p => p.program === programNr && !p.isDrumPreset(allowXGDrums));
685
- }
686
- if (preset)
687
- {
688
- SpessaSynthWarn(
689
- `%cPreset ${bankNr}.${programNr} not found. Replaced with %c${preset.presetName} (${preset.bank}.${preset.program})`,
690
- consoleColors.warn,
691
- consoleColors.recognized
692
- );
693
- }
694
-
695
- // no preset, use the first one available
696
- if (!preset)
697
- {
698
- SpessaSynthWarn(`Preset ${programNr} not found. Defaulting to`, this.presets[0].presetName);
699
- preset = this.presets[0];
700
- }
701
- return preset;
702
- }
703
-
704
- /**
705
- * gets preset by name
706
- * @param presetName {string}
707
- * @returns {BasicPreset}
708
- */
709
- getPresetByName(presetName)
710
- {
711
- let preset = this.presets.find(p => p.presetName === presetName);
712
- if (!preset)
713
- {
714
- SpessaSynthWarn("Preset not found. Defaulting to:", this.presets[0].presetName);
715
- preset = this.presets[0];
716
- }
717
- return preset;
718
- }
719
-
720
- /**
721
- * @param error {string}
722
- */
723
- parsingError(error)
724
- {
725
- throw new Error(`SF parsing error: ${error} The file may be corrupted.`);
726
- }
727
-
728
- destroySoundBank()
729
- {
730
- delete this.presets;
731
- delete this.instruments;
732
- delete this.samples;
733
- delete this.soundFontInfo;
734
- }
735
- }
736
-
737
- BasicSoundBank.prototype.write = write;
738
- BasicSoundBank.prototype.writeDLS = writeDLS;
739
-
740
- export { BasicSoundBank };