spessasynth_core 3.26.17 → 3.26.19

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 (62) hide show
  1. package/index.js +5 -3
  2. package/package.json +1 -1
  3. package/src/externals/README.md +6 -0
  4. package/src/sequencer/README.md +5 -1
  5. package/src/soundfont/README.md +6 -8
  6. package/src/soundfont/basic_soundfont/basic_global_zone.js +16 -0
  7. package/src/soundfont/basic_soundfont/basic_instrument.js +26 -22
  8. package/src/soundfont/basic_soundfont/basic_instrument_zone.js +35 -0
  9. package/src/soundfont/basic_soundfont/basic_preset.js +53 -42
  10. package/src/soundfont/basic_soundfont/basic_preset_zone.js +30 -0
  11. package/src/soundfont/basic_soundfont/{basic_soundfont.js → basic_soundbank.js} +96 -55
  12. package/src/soundfont/basic_soundfont/basic_zone.js +73 -5
  13. package/src/soundfont/basic_soundfont/generator.js +4 -163
  14. package/src/soundfont/basic_soundfont/generator_types.js +151 -0
  15. package/src/soundfont/basic_soundfont/modulator.js +217 -65
  16. package/src/soundfont/basic_soundfont/write_dls/art2.js +3 -2
  17. package/src/soundfont/basic_soundfont/write_dls/combine_zones.js +28 -45
  18. package/src/soundfont/basic_soundfont/write_dls/ins.js +11 -27
  19. package/src/soundfont/basic_soundfont/write_dls/modulator_converter.js +2 -2
  20. package/src/soundfont/basic_soundfont/write_dls/rgn2.js +2 -2
  21. package/src/soundfont/basic_soundfont/write_sf2/ibag.js +22 -19
  22. package/src/soundfont/basic_soundfont/write_sf2/igen.js +33 -29
  23. package/src/soundfont/basic_soundfont/write_sf2/imod.js +26 -16
  24. package/src/soundfont/basic_soundfont/write_sf2/inst.js +4 -5
  25. package/src/soundfont/basic_soundfont/write_sf2/pbag.js +21 -18
  26. package/src/soundfont/basic_soundfont/write_sf2/pgen.js +32 -29
  27. package/src/soundfont/basic_soundfont/write_sf2/phdr.js +4 -2
  28. package/src/soundfont/basic_soundfont/write_sf2/pmod.js +26 -16
  29. package/src/soundfont/basic_soundfont/write_sf2/write.js +14 -8
  30. package/src/soundfont/dls/articulator_converter.js +9 -3
  31. package/src/soundfont/dls/dls_preset.js +2 -3
  32. package/src/soundfont/dls/dls_soundfont.js +2 -3
  33. package/src/soundfont/dls/dls_sources.js +7 -6
  34. package/src/soundfont/dls/dls_zone.js +13 -16
  35. package/src/soundfont/dls/read_articulation.js +2 -1
  36. package/src/soundfont/dls/read_instrument.js +7 -10
  37. package/src/soundfont/dls/read_lart.js +5 -5
  38. package/src/soundfont/dls/read_region.js +3 -3
  39. package/src/soundfont/read_sf2/instruments.js +10 -1
  40. package/src/soundfont/read_sf2/modulators.js +10 -23
  41. package/src/soundfont/read_sf2/presets.js +10 -1
  42. package/src/soundfont/read_sf2/soundfont.js +3 -5
  43. package/src/soundfont/read_sf2/zones.js +30 -70
  44. package/src/synthetizer/README.md +3 -3
  45. package/src/synthetizer/audio_engine/README.md +1 -1
  46. package/src/synthetizer/audio_engine/engine_components/compute_modulator.js +20 -19
  47. package/src/synthetizer/audio_engine/engine_components/dynamic_modulator_system.js +11 -3
  48. package/src/synthetizer/audio_engine/engine_components/lowpass_filter.js +1 -1
  49. package/src/synthetizer/audio_engine/engine_components/midi_audio_channel.js +1 -1
  50. package/src/synthetizer/audio_engine/engine_components/modulation_envelope.js +1 -1
  51. package/src/synthetizer/audio_engine/engine_components/stereo_panner.js +1 -1
  52. package/src/synthetizer/audio_engine/engine_components/voice.js +7 -10
  53. package/src/synthetizer/audio_engine/engine_components/volume_envelope.js +2 -1
  54. package/src/synthetizer/audio_engine/engine_methods/controller_control/reset_controllers.js +0 -4
  55. package/src/synthetizer/audio_engine/engine_methods/data_entry/awe32.js +1 -1
  56. package/src/synthetizer/audio_engine/engine_methods/data_entry/data_entry_coarse.js +7 -0
  57. package/src/synthetizer/audio_engine/engine_methods/note_on.js +1 -1
  58. package/src/synthetizer/audio_engine/engine_methods/render_voice.js +1 -1
  59. package/src/synthetizer/audio_engine/engine_methods/stopping_notes/kill_note.js +1 -1
  60. package/src/synthetizer/audio_engine/engine_methods/system_exclusive.js +2 -1
  61. package/src/utils/README.md +5 -2
  62. package/src/soundfont/basic_soundfont/basic_zones.js +0 -43
package/index.js CHANGED
@@ -18,12 +18,12 @@ import {
18
18
  import { SynthesizerSnapshot } from "./src/synthetizer/audio_engine/snapshot/synthesizer_snapshot.js";
19
19
  import { ChannelSnapshot } from "./src/synthetizer/audio_engine/snapshot/channel_snapshot.js";
20
20
 
21
- import { BasicSoundBank } from "./src/soundfont/basic_soundfont/basic_soundfont.js";
21
+ import { BasicSoundBank } from "./src/soundfont/basic_soundfont/basic_soundbank.js";
22
22
  import { BasicSample } from "./src/soundfont/basic_soundfont/basic_sample.js";
23
- import { BasicInstrumentZone, BasicPresetZone } from "./src/soundfont/basic_soundfont/basic_zones.js";
23
+ import { BasicPresetZone } from "./src/soundfont/basic_soundfont/basic_preset_zone.js";
24
24
  import { BasicInstrument } from "./src/soundfont/basic_soundfont/basic_instrument.js";
25
25
  import { BasicPreset } from "./src/soundfont/basic_soundfont/basic_preset.js";
26
- import { Generator, generatorTypes } from "./src/soundfont/basic_soundfont/generator.js";
26
+ import { Generator } from "./src/soundfont/basic_soundfont/generator.js";
27
27
  import { Modulator, modulatorCurveTypes, modulatorSources } from "./src/soundfont/basic_soundfont/modulator.js";
28
28
  import { loadSoundFont } from "./src/soundfont/load_soundfont.js";
29
29
 
@@ -51,6 +51,8 @@ import { consoleColors } from "./src/utils/other.js";
51
51
  import { inflateSync } from "./src/externals/fflate/fflate.min.js";
52
52
  import { DLSDestinations } from "./src/soundfont/dls/dls_destinations.js";
53
53
  import { DLSSources } from "./src/soundfont/dls/dls_sources.js";
54
+ import { generatorTypes } from "./src/soundfont/basic_soundfont/generator_types.js";
55
+ import { BasicInstrumentZone } from "./src/soundfont/basic_soundfont/basic_instrument_zone.js";
54
56
  // you shouldn't use these...
55
57
  const SpessaSynthCoreUtils = {
56
58
  consoleColors,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "spessasynth_core",
3
- "version": "3.26.17",
3
+ "version": "3.26.19",
4
4
  "description": "MIDI and SoundFont2/DLS library with no compromises",
5
5
  "main": "index.js",
6
6
  "type": "module",
@@ -0,0 +1,6 @@
1
+ ## This is the external dependencies' folder.
2
+
3
+ The code here contains third-party libraries used by SpessaSynth.
4
+
5
+ - `stbvorbis_sync` - Vorbis audio decoder for SF3 soundfonts
6
+ - `fflate` - Used for decompressing XMF files. It is also used to compress the reverb buffer in spessasynth_lib
@@ -2,4 +2,8 @@
2
2
 
3
3
  The code here is responsible for playing back the parsed MIDI sequence with the synthesizer.
4
4
 
5
- - `sequencer_engine` - the core sequencer engine
5
+ - `sequencer_engine.js` - the core sequencer engine
6
+ - `play.js` - handles playback control and timing
7
+ - `song_control.js` - manages song state and control
8
+ - `process_event.js` - processes MIDI events during playback
9
+ - `process_tick.js` - processes a single MIDI tick (think of it like a rendering quantum of the sequencer)
@@ -1,13 +1,11 @@
1
- ## This is the SoundFont2 parsing library.
1
+ ## This is the SoundFont and DLS parsing library.
2
2
 
3
- The code here is responsible for parsing the SoundFont2 file and
3
+ The code here is responsible for parsing the SoundFont2/DLS file and
4
4
  providing an easy way to get the data out.
5
- Default modulators are also stored here (in `modulators.js`)
6
5
 
7
- `basic_soundfont` folder contains the classes that represent the soundfont file.
6
+ - `basic_soundfont` folder contains the classes that represent a sound bank file.
7
+ It also contains the code for writing the files.
8
8
 
9
- `read_sf2` folder contains the code for reading an `.sf2` file.
9
+ - `read_sf2` folder contains the code for reading a `.sf2` file.
10
10
 
11
- `write` folder contains the code for writing out an `.sf2` file.
12
-
13
- `dls` folder contains the code for reading a `.dls` file (and converting in into a soundfont representation).
11
+ - `dls` folder contains the code for reading a `.dls` file (and converting in into a soundfont representation).
@@ -0,0 +1,16 @@
1
+ import { BasicZone } from "./basic_zone.js";
2
+
3
+ export class BasicGlobalZone extends BasicZone
4
+ {
5
+
6
+ /**
7
+ * @param zone {BasicZone}
8
+ */
9
+ copyFrom(zone)
10
+ {
11
+ this.keyRange = { ...zone.keyRange };
12
+ this.velRange = { ...zone.velRange };
13
+ this.generators = [...zone.generators];
14
+ this.modulators = [...zone.modulators];
15
+ }
16
+ }
@@ -1,3 +1,5 @@
1
+ import { BasicGlobalZone } from "./basic_global_zone.js";
2
+
1
3
  export class BasicInstrument
2
4
  {
3
5
  /**
@@ -9,9 +11,16 @@ export class BasicInstrument
9
11
  /**
10
12
  * The instrument's zones
11
13
  * @type {BasicInstrumentZone[]}
14
+ * @readonly
12
15
  */
13
16
  instrumentZones = [];
14
17
 
18
+ /**
19
+ * Instrument's global zone
20
+ * @type {BasicGlobalZone}
21
+ */
22
+ globalZone = new BasicGlobalZone();
23
+
15
24
  /**
16
25
  * Instrument's use count, used for trimming
17
26
  * @type {number}
@@ -27,6 +36,15 @@ export class BasicInstrument
27
36
  return this._useCount;
28
37
  }
29
38
 
39
+ /**
40
+ * @param zones {BasicInstrumentZone}
41
+ */
42
+ addZones(...zones)
43
+ {
44
+ zones.forEach(z => z.useCount++);
45
+ this.instrumentZones.push(...zones);
46
+ }
47
+
30
48
  addUseCount()
31
49
  {
32
50
  this._useCount++;
@@ -36,16 +54,10 @@ export class BasicInstrument
36
54
  removeUseCount()
37
55
  {
38
56
  this._useCount--;
39
- for (let i = 0; i < this.instrumentZones.length; i++)
40
- {
41
- if (this.safeDeleteZone(i))
42
- {
43
- i--;
44
- }
45
- }
57
+ this.instrumentZones.forEach(z => z.useCount--);
46
58
  }
47
59
 
48
- deleteInstrument()
60
+ deleteZones()
49
61
  {
50
62
  this.instrumentZones.forEach(z => z.deleteZone());
51
63
  this.instrumentZones.length = 0;
@@ -53,25 +65,17 @@ export class BasicInstrument
53
65
 
54
66
  /**
55
67
  * @param index {number}
56
- * @returns {boolean} is the zone has been deleted
68
+ * @returns {boolean} if deleted
57
69
  */
58
- safeDeleteZone(index)
70
+ deleteZone(index)
59
71
  {
60
- this.instrumentZones[index].useCount--;
61
- if (this.instrumentZones[index].useCount < 1)
72
+ const zone = this.instrumentZones[index];
73
+ if (zone.useCount < 1)
62
74
  {
63
- this.deleteZone(index);
75
+ zone.deleteZone();
76
+ this.instrumentZones.splice(index, 1);
64
77
  return true;
65
78
  }
66
79
  return false;
67
80
  }
68
-
69
- /**
70
- * @param index {number}
71
- */
72
- deleteZone(index)
73
- {
74
- this.instrumentZones[index].deleteZone();
75
- this.instrumentZones.splice(index, 1);
76
- }
77
81
  }
@@ -0,0 +1,35 @@
1
+ import { BasicZone } from "./basic_zone.js";
2
+
3
+ export class BasicInstrumentZone extends BasicZone
4
+ {
5
+ /**
6
+ * Zone's sample.
7
+ * @type {BasicSample}
8
+ */
9
+ sample;
10
+
11
+ /**
12
+ * For tracking on the individual zone level, since multiple presets can refer to the same instrument
13
+ * @type {number}
14
+ */
15
+ useCount = 0;
16
+
17
+ /**
18
+ * @param sample {BasicSample}
19
+ */
20
+ setSample(sample)
21
+ {
22
+ this.sample = sample;
23
+ this.sample.useCount++;
24
+ }
25
+
26
+ deleteZone()
27
+ {
28
+ this.sample.useCount--;
29
+ }
30
+
31
+ hasSample()
32
+ {
33
+ return !!this.sample;
34
+ }
35
+ }
@@ -4,13 +4,13 @@
4
4
  * presetGenerators: Generator[],
5
5
  * modulators: Modulator[],
6
6
  * sample: BasicSample,
7
- * sampleID: number,
8
7
  * }} SampleAndGenerators
9
8
  */
10
- import { generatorTypes } from "./generator.js";
11
9
  import { Modulator } from "./modulator.js";
12
10
  import { isXGDrums } from "../../utils/xg_hacks.js";
13
11
 
12
+ import { BasicGlobalZone } from "./basic_global_zone.js";
13
+
14
14
  export class BasicPreset
15
15
  {
16
16
  /**
@@ -44,6 +44,12 @@ export class BasicPreset
44
44
  */
45
45
  presetZones = [];
46
46
 
47
+ /**
48
+ * Preset's global zone
49
+ * @type {BasicGlobalZone}
50
+ */
51
+ globalZone = new BasicGlobalZone();
52
+
47
53
  /**
48
54
  * unused metadata
49
55
  * @type {number}
@@ -69,6 +75,14 @@ export class BasicPreset
69
75
  this.parentSoundBank = parentSoundBank;
70
76
  }
71
77
 
78
+ /**
79
+ * @param zones {BasicPresetZone}
80
+ */
81
+ addZones(...zones)
82
+ {
83
+ this.presetZones.push(...zones);
84
+ }
85
+
72
86
  /**
73
87
  * @param allowXG {boolean}
74
88
  * @param allowSFX {boolean}
@@ -171,62 +185,60 @@ export class BasicPreset
171
185
  * global zone is always first, so it or nothing
172
186
  * @type {Generator[]}
173
187
  */
174
- let globalPresetGenerators = this.presetZones[0].isGlobal ? [...this.presetZones[0].generators] : [];
188
+ let globalPresetGenerators = [...this.globalZone.generators];
175
189
 
176
190
  /**
177
191
  * @type {Modulator[]}
178
192
  */
179
- let globalPresetModulators = this.presetZones[0].isGlobal ? [...this.presetZones[0].modulators] : [];
180
- const globalKeyRange = this.presetZones[0].isGlobal ? this.presetZones[0].keyRange : { min: 0, max: 127 };
181
- const globalVelRange = this.presetZones[0].isGlobal ? this.presetZones[0].velRange : { min: 0, max: 127 };
193
+ let globalPresetModulators = [...this.globalZone.modulators];
194
+ const globalKeyRange = this.globalZone.keyRange;
195
+ const globalVelRange = this.globalZone.velRange;
182
196
 
183
197
  // find the preset zones in range
184
198
  let presetZonesInRange = this.presetZones.filter(currentZone =>
185
- (
186
- isInRange(
187
- currentZone.hasKeyRange ? currentZone.keyRange : globalKeyRange,
188
- midiNote
189
- )
190
- &&
191
- isInRange(
192
- currentZone.hasVelRange ? currentZone.velRange : globalVelRange,
193
- velocity
194
- )
195
- ) && !currentZone.isGlobal);
199
+ isInRange(
200
+ currentZone.hasKeyRange ? currentZone.keyRange : globalKeyRange,
201
+ midiNote
202
+ )
203
+ &&
204
+ isInRange(
205
+ currentZone.hasVelRange ? currentZone.velRange : globalVelRange,
206
+ velocity
207
+ )
208
+ );
196
209
 
197
- presetZonesInRange.forEach(zone =>
210
+ presetZonesInRange.forEach(presetZone =>
198
211
  {
212
+ const instrument = presetZone.instrument;
199
213
  // the global zone is already taken into account earlier
200
- if (zone.instrument.instrumentZones.length < 1)
214
+ if (instrument.instrumentZones.length < 1)
201
215
  {
202
216
  return;
203
217
  }
204
- let presetGenerators = zone.generators;
205
- let presetModulators = zone.modulators;
206
- const firstZone = zone.instrument.instrumentZones[0];
218
+ let presetGenerators = presetZone.generators;
219
+ let presetModulators = presetZone.modulators;
207
220
  /**
208
221
  * global zone is always first, so it or nothing
209
222
  * @type {Generator[]}
210
223
  */
211
- let globalInstrumentGenerators = firstZone.isGlobal ? [...firstZone.generators] : [];
212
- let globalInstrumentModulators = firstZone.isGlobal ? [...firstZone.modulators] : [];
213
- const globalKeyRange = firstZone.isGlobal ? firstZone.keyRange : { min: 0, max: 127 };
214
- const globalVelRange = firstZone.isGlobal ? firstZone.velRange : { min: 0, max: 127 };
224
+ let globalInstrumentGenerators = [...instrument.globalZone.generators];
225
+ let globalInstrumentModulators = [...instrument.globalZone.modulators];
226
+ const globalKeyRange = instrument.globalZone.keyRange;
227
+ const globalVelRange = instrument.globalZone.velRange;
215
228
 
216
229
 
217
- let instrumentZonesInRange = zone.instrument.instrumentZones
230
+ let instrumentZonesInRange = instrument.instrumentZones
218
231
  .filter(currentZone =>
219
- (
220
- isInRange(
221
- currentZone.hasKeyRange ? currentZone.keyRange : globalKeyRange,
222
- midiNote
223
- )
224
- &&
225
- isInRange(
226
- currentZone.hasVelRange ? currentZone.velRange : globalVelRange,
227
- velocity
228
- )
229
- ) && !currentZone.isGlobal
232
+
233
+ isInRange(
234
+ currentZone.hasKeyRange ? currentZone.keyRange : globalKeyRange,
235
+ midiNote
236
+ )
237
+ &&
238
+ isInRange(
239
+ currentZone.hasVelRange ? currentZone.velRange : globalVelRange,
240
+ velocity
241
+ )
230
242
  );
231
243
 
232
244
  instrumentZonesInRange.forEach(instrumentZone =>
@@ -275,7 +287,8 @@ export class BasicPreset
275
287
  if (identicalInstrumentModulator !== -1)
276
288
  {
277
289
  // sum the amounts
278
- // (this makes a new modulator because otherwise it would overwrite the one in the soundfont!
290
+ // this makes a new modulator
291
+ // because otherwise it would overwrite the one in the soundfont!
279
292
  finalModulatorList[identicalInstrumentModulator] = finalModulatorList[identicalInstrumentModulator].sumTransform(
280
293
  mod);
281
294
  }
@@ -291,9 +304,7 @@ export class BasicPreset
291
304
  instrumentGenerators: instrumentGenerators,
292
305
  presetGenerators: presetGenerators,
293
306
  modulators: finalModulatorList,
294
- sample: instrumentZone.sample,
295
- sampleID: instrumentZone.generators.find(
296
- g => g.generatorType === generatorTypes.sampleID).generatorValue
307
+ sample: instrumentZone.sample
297
308
  });
298
309
  });
299
310
  });
@@ -0,0 +1,30 @@
1
+ import { BasicZone } from "./basic_zone.js";
2
+
3
+ export class BasicPresetZone extends BasicZone
4
+ {
5
+ /**
6
+ * Zone's instrument
7
+ * @type {BasicInstrument}
8
+ */
9
+ instrument;
10
+
11
+ deleteZone()
12
+ {
13
+ this.instrument.removeUseCount();
14
+ }
15
+
16
+ /**
17
+ * @param instrument {BasicInstrument}
18
+ */
19
+ setInstrument(instrument)
20
+ {
21
+ this.instrument = instrument;
22
+ this.instrument.addUseCount();
23
+ }
24
+
25
+
26
+ hasInstrument()
27
+ {
28
+ return !!this.instrument;
29
+ }
30
+ }
@@ -10,15 +10,20 @@ import { write } from "./write_sf2/write.js";
10
10
  import { defaultModulators, Modulator } from "./modulator.js";
11
11
  import { writeDLS } from "./write_dls/write_dls.js";
12
12
  import { BasicSample } from "./basic_sample.js";
13
- import { BasicInstrumentZone, BasicPresetZone } from "./basic_zones.js";
14
- import { Generator, generatorTypes } from "./generator.js";
13
+ import { BasicPresetZone } from "./basic_preset_zone.js";
14
+ import { Generator } from "./generator.js";
15
15
  import { BasicInstrument } from "./basic_instrument.js";
16
16
  import { BasicPreset } from "./basic_preset.js";
17
17
  import { isXGDrums } from "../../utils/xg_hacks.js";
18
+ import { generatorTypes } from "./generator_types.js";
19
+ import { BasicInstrumentZone } from "./basic_instrument_zone.js";
20
+ import { BasicGlobalZone } from "./basic_global_zone.js";
18
21
 
22
+ /**
23
+ * Represents a single sound bank, be it DLS or SF2.
24
+ */
19
25
  class BasicSoundBank
20
26
  {
21
-
22
27
  /**
23
28
  * Soundfont's info stored as name: value. ifil and iver are stored as string representation of float (e.g., 2.1)
24
29
  * @type {Object<string, string|IndexedByteArray>}
@@ -62,15 +67,47 @@ class BasicSoundBank
62
67
  isXGBank = false;
63
68
 
64
69
  /**
65
- * Creates a new basic soundfont template
70
+ * Creates a new basic soundfont template (or copies)
66
71
  * @param data {undefined|{presets: BasicPreset[], info: Object<string, string>}}
67
72
  */
68
73
  constructor(data = undefined)
69
74
  {
70
75
  if (data?.presets)
71
76
  {
72
- this.presets.push(...data.presets);
73
77
  this.soundFontInfo = data.info;
78
+ this.addPresets(...data.presets);
79
+ /**
80
+ * @type {BasicInstrument[]}
81
+ */
82
+ const instrumentList = [];
83
+ for (const preset of data.presets)
84
+ {
85
+ for (const zone of preset.presetZones)
86
+ {
87
+ if (!instrumentList.includes(zone.instrument))
88
+ {
89
+ instrumentList.push(zone.instrument);
90
+ }
91
+ }
92
+ }
93
+ this.addInstruments(...instrumentList);
94
+
95
+ /**
96
+ * @type {BasicSample[]}
97
+ */
98
+ const sampleList = [];
99
+
100
+ for (const instrument of instrumentList)
101
+ {
102
+ for (const zone of instrument.instrumentZones)
103
+ {
104
+ if (!sampleList.includes(zone.sample))
105
+ {
106
+ sampleList.push(zone.sample);
107
+ }
108
+ }
109
+ }
110
+ this.addSamples(...sampleList);
74
111
  }
75
112
  }
76
113
 
@@ -122,44 +159,74 @@ class BasicSoundBank
122
159
  {
123
160
  sample.sampleData[i] = (i / 128) * 2 - 1;
124
161
  }
125
- font.samples.push(sample);
162
+ font.addSamples(sample);
126
163
 
127
- const gZone = new BasicInstrumentZone();
128
- gZone.isGlobal = true;
129
- gZone.generators.push(new Generator(generatorTypes.initialAttenuation, 375));
130
- gZone.generators.push(new Generator(generatorTypes.releaseVolEnv, -1000));
131
- gZone.generators.push(new Generator(generatorTypes.sampleModes, 1));
164
+ const gZone = new BasicGlobalZone();
165
+ gZone.addGenerators(
166
+ new Generator(generatorTypes.initialAttenuation, 375),
167
+ new Generator(generatorTypes.releaseVolEnv, -1000),
168
+ new Generator(generatorTypes.sampleModes, 1)
169
+ );
132
170
 
133
171
  const zone1 = new BasicInstrumentZone();
134
- zone1.sample = sample;
172
+ zone1.setSample(sample);
135
173
 
136
174
  const zone2 = new BasicInstrumentZone();
137
- zone2.sample = sample;
138
- zone2.generators.push(new Generator(generatorTypes.fineTune, -9));
175
+ zone2.setSample(sample);
176
+ zone2.addGenerators(new Generator(generatorTypes.fineTune, -9));
139
177
 
140
178
 
141
179
  const inst = new BasicInstrument();
142
180
  inst.instrumentName = "Saw Wave";
143
- inst.instrumentZones.push(gZone);
144
- inst.instrumentZones.push(zone1);
145
- inst.instrumentZones.push(zone2);
146
- font.instruments.push(inst);
181
+ inst.globalZone = gZone;
182
+ inst.addZones(zone1, zone2);
183
+ font.addInstruments(inst);
147
184
 
148
185
  const pZone = new BasicPresetZone();
149
- pZone.instrument = inst;
186
+ pZone.setInstrument(inst);
150
187
 
151
188
  const preset = new BasicPreset(font);
152
189
  preset.presetName = "Saw Wave";
153
- preset.presetZones.push(pZone);
154
- font.presets.push(preset);
190
+ preset.addZones(pZone);
191
+ font.addPresets(preset);
155
192
 
156
193
  font.soundFontInfo["ifil"] = "2.1";
157
194
  font.soundFontInfo["isng"] = "EMU8000";
158
195
  font.soundFontInfo["INAM"] = "Dummy";
159
- font._parseInternal();
196
+ font.flush();
160
197
  return font.write().buffer;
161
198
  }
162
199
 
200
+ /**
201
+ * @param preset {BasicPreset}
202
+ */
203
+ addPresets(...preset)
204
+ {
205
+ this.presets.push(...preset);
206
+ }
207
+
208
+ flush()
209
+ {
210
+ this.presets.sort((a, b) => (a.program - b.program) + (a.bank - b.bank));
211
+ this._parseInternal();
212
+ }
213
+
214
+ /**
215
+ * @param instrument {BasicInstrument}
216
+ */
217
+ addInstruments(...instrument)
218
+ {
219
+ this.instruments.push(...instrument);
220
+ }
221
+
222
+ /**
223
+ * @param sample {BasicSample}
224
+ */
225
+ addSamples(...sample)
226
+ {
227
+ this.samples.push(...sample);
228
+ }
229
+
163
230
  /**
164
231
  * parses the bank after loading is done
165
232
  * @protected
@@ -209,7 +276,7 @@ class BasicSoundBank
209
276
  const soundfont = this;
210
277
 
211
278
  /**
212
- * @param instrument {Instrument}
279
+ * @param instrument {BasicInstrument}
213
280
  * @param combos {{key: number, velocity: number}[]}
214
281
  * @returns {number}
215
282
  */
@@ -219,10 +286,6 @@ class BasicSoundBank
219
286
  for (let iZoneIndex = 0; iZoneIndex < instrument.instrumentZones.length; iZoneIndex++)
220
287
  {
221
288
  const iZone = instrument.instrumentZones[iZoneIndex];
222
- if (iZone.isGlobal)
223
- {
224
- continue;
225
- }
226
289
  const iKeyRange = iZone.keyRange;
227
290
  const iVelRange = iZone.velRange;
228
291
  let isIZoneUsed = false;
@@ -240,14 +303,13 @@ class BasicSoundBank
240
303
  if (!isIZoneUsed)
241
304
  {
242
305
  SpessaSynthInfo(
243
- `%c${iZone.sample.sampleName} %cremoved from %c${instrument.instrumentName}%c. Use count: %c${iZone.useCount - 1}`,
306
+ `%c${iZone.sample.sampleName} %cremoved from %c${instrument.instrumentName}%c.`,
244
307
  consoleColors.recognized,
245
308
  consoleColors.info,
246
309
  consoleColors.recognized,
247
- consoleColors.info,
248
- consoleColors.recognized
310
+ consoleColors.info
249
311
  );
250
- if (instrument.safeDeleteZone(iZoneIndex))
312
+ if (instrument.deleteZone(iZoneIndex))
251
313
  {
252
314
  trimmedIZones++;
253
315
  iZoneIndex--;
@@ -293,6 +355,7 @@ class BasicSoundBank
293
355
  consoleColors.info
294
356
  );
295
357
  soundfont.deletePreset(p);
358
+ soundfont.removeUnusedElements();
296
359
  presetIndex--;
297
360
  }
298
361
  else
@@ -316,10 +379,6 @@ class BasicSoundBank
316
379
  for (let zoneIndex = 0; zoneIndex < p.presetZones.length; zoneIndex++)
317
380
  {
318
381
  const zone = p.presetZones[zoneIndex];
319
- if (zone.isGlobal)
320
- {
321
- continue;
322
- }
323
382
  const keyRange = zone.keyRange;
324
383
  const velRange = zone.velRange;
325
384
  // check if any of the combos matches the zone
@@ -384,13 +443,7 @@ class BasicSoundBank
384
443
  {
385
444
  if (i.useCount < 1)
386
445
  {
387
- i.instrumentZones.forEach(z =>
388
- {
389
- if (!z.isGlobal)
390
- {
391
- z.sample.useCount--;
392
- }
393
- });
446
+ i.deleteZones();
394
447
  }
395
448
  });
396
449
  this.instruments = this.instruments.filter(i => i.useCount > 0);
@@ -407,8 +460,7 @@ class BasicSoundBank
407
460
  throw new Error(`Cannot delete an instrument that has ${instrument.useCount} usages.`);
408
461
  }
409
462
  this.instruments.splice(this.instruments.indexOf(instrument), 1);
410
- instrument.deleteInstrument();
411
- this.removeUnusedElements();
463
+ instrument.deleteZones();
412
464
  }
413
465
 
414
466
  /**
@@ -418,7 +470,6 @@ class BasicSoundBank
418
470
  {
419
471
  preset.deletePreset();
420
472
  this.presets.splice(this.presets.indexOf(preset), 1);
421
- this.removeUnusedElements();
422
473
  }
423
474
 
424
475
  /**
@@ -431,7 +482,6 @@ class BasicSoundBank
431
482
  throw new Error(`Cannot delete sample that has ${sample.useCount} usages.`);
432
483
  }
433
484
  this.samples.splice(this.samples.indexOf(sample), 1);
434
- this.removeUnusedElements();
435
485
  }
436
486
 
437
487
  /**
@@ -563,15 +613,6 @@ class BasicSoundBank
563
613
  delete this.instruments;
564
614
  delete this.samples;
565
615
  }
566
-
567
- // noinspection JSUnusedGlobalSymbols
568
- /**
569
- * Clears the cache for all the presets
570
- */
571
- clearCache()
572
- {
573
- this.presets.forEach(p => p.clearCache());
574
- }
575
616
  }
576
617
 
577
618
  BasicSoundBank.prototype.write = write;