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
@@ -1,13 +1,13 @@
1
1
  import { combineArrays, IndexedByteArray } from "../../../utils/indexed_array.js";
2
2
  import { writeDword, writeWord } from "../../../utils/byte_functions/little_endian.js";
3
- import { generatorTypes } from "../generator.js";
4
3
  import { writeRIFFOddSize } from "../riff_chunk.js";
5
4
  import { writeWavesample } from "./wsmp.js";
6
5
  import { writeArticulator } from "./art2.js";
6
+ import { generatorTypes } from "../generator_types.js";
7
7
 
8
8
  /**
9
9
  * @param zone {BasicInstrumentZone}
10
- * @param globalZone {BasicInstrumentZone}
10
+ * @param globalZone {BasicGlobalZone}
11
11
  * @this {BasicSoundBank}
12
12
  * @returns {IndexedByteArray}
13
13
  */
@@ -2,6 +2,8 @@ import { IndexedByteArray } from "../../../utils/indexed_array.js";
2
2
  import { writeWord } from "../../../utils/byte_functions/little_endian.js";
3
3
  import { RiffChunk, writeRIFFChunk } from "../riff_chunk.js";
4
4
 
5
+ const BAG_SIZE = 4;
6
+
5
7
  /**
6
8
  * @this {BasicSoundBank}
7
9
  * @returns {IndexedByteArray}
@@ -9,36 +11,37 @@ import { RiffChunk, writeRIFFChunk } from "../riff_chunk.js";
9
11
  export function getIBAG()
10
12
  {
11
13
  // write all ibag with their start indexes as they were changed in getIGEN() and getIMOD()
12
- const ibagsize = this.instruments.reduce((sum, i) => i.instrumentZones.length * 4 + sum, 4);
14
+ const ibagsize = this.instruments.reduce(
15
+ (sum, i) =>
16
+ // +1 because global zone
17
+ (i.instrumentZones.length + 1) * BAG_SIZE + sum,
18
+ BAG_SIZE
19
+ );
13
20
  const ibagdata = new IndexedByteArray(ibagsize);
14
- let zoneID = 0;
15
21
  let generatorIndex = 0;
16
22
  let modulatorIndex = 0;
23
+ /**
24
+ * @param z {BasicZone}
25
+ */
26
+ const writeZone = z =>
27
+ {
28
+ writeWord(ibagdata, generatorIndex);
29
+ writeWord(ibagdata, modulatorIndex);
30
+ generatorIndex += z.generators.length;
31
+ modulatorIndex += z.modulators.length;
32
+ };
33
+
17
34
  for (const inst of this.instruments)
18
35
  {
19
- // ensure that the first zone is global
20
- const zones = inst.instrumentZones.filter(z => !z.isGlobal);
21
- const global = inst.instrumentZones.filter(z => z.isGlobal);
22
- // only take the first one
23
- if (global?.[0])
36
+ writeZone(inst.globalZone);
37
+ for (const ibag of inst.instrumentZones)
24
38
  {
25
- zones.unshift(global?.[0]);
26
- }
27
- inst.instrumentZoneIndex = zoneID;
28
- for (const ibag of zones)
29
- {
30
- ibag.zoneID = zoneID;
31
- writeWord(ibagdata, generatorIndex);
32
- writeWord(ibagdata, modulatorIndex);
33
- generatorIndex += ibag.generators.length;
34
- modulatorIndex += ibag.modulators.length;
35
- zoneID++;
39
+ writeZone(ibag);
36
40
  }
37
41
  }
38
42
  // write the terminal IBAG
39
43
  writeWord(ibagdata, generatorIndex);
40
44
  writeWord(ibagdata, modulatorIndex);
41
-
42
45
  return writeRIFFChunk(new RiffChunk(
43
46
  "ibag",
44
47
  ibagdata.length,
@@ -1,8 +1,8 @@
1
1
  import { writeDword, writeWord } from "../../../utils/byte_functions/little_endian.js";
2
2
  import { IndexedByteArray } from "../../../utils/indexed_array.js";
3
3
  import { RiffChunk, writeRIFFChunk } from "../riff_chunk.js";
4
-
5
- import { Generator, generatorTypes } from "../generator.js";
4
+ import { GEN_BYTE_SIZE, Generator } from "../generator.js";
5
+ import { generatorTypes } from "../generator_types.js";
6
6
 
7
7
  /**
8
8
  * @this {BasicSoundBank}
@@ -11,22 +11,23 @@ import { Generator, generatorTypes } from "../generator.js";
11
11
  export function getIGEN()
12
12
  {
13
13
  // go through all instruments -> zones and write generators sequentially (add 4 for terminal)
14
- let igensize = 4;
14
+ let igensize = GEN_BYTE_SIZE;
15
15
  for (const inst of this.instruments)
16
16
  {
17
+ igensize += inst.globalZone.generators.length * GEN_BYTE_SIZE;
17
18
  igensize += inst.instrumentZones.reduce((sum, z) =>
18
19
  {
19
20
  // clear sample and range generators before determining the size
20
- z.generators = z.generators.filter(g =>
21
+ z.generators = (z.generators.filter(g =>
21
22
  g.generatorType !== generatorTypes.sampleID &&
22
23
  g.generatorType !== generatorTypes.keyRange &&
23
24
  g.generatorType !== generatorTypes.velRange
24
- );
25
+ ));
25
26
  // add sample and ranges if necessary
26
- // unshift vel then key (to make key first) and the instrument is last
27
+ // unshift vel then key (to make key first) and the sample is last
27
28
  if (z.hasVelRange)
28
29
  {
29
- z.generators.unshift(new Generator(
30
+ z.prependGenerator(new Generator(
30
31
  generatorTypes.velRange,
31
32
  z.velRange.max << 8 | Math.max(z.velRange.min, 0),
32
33
  false
@@ -34,44 +35,47 @@ export function getIGEN()
34
35
  }
35
36
  if (z.hasKeyRange)
36
37
  {
37
- z.generators.unshift(new Generator(
38
+ z.prependGenerator(new Generator(
38
39
  generatorTypes.keyRange,
39
40
  z.keyRange.max << 8 | Math.max(z.keyRange.min, 0),
40
41
  false
41
42
  ));
42
43
  }
43
- if (!z.isGlobal)
44
- {
45
- // write sample
46
- z.generators.push(new Generator(
47
- generatorTypes.sampleID,
48
- this.samples.indexOf(z.sample),
49
- false
50
- ));
51
- }
52
- return z.generators.length * 4 + sum;
44
+ // add sample id
45
+ z.addGenerators(new Generator(
46
+ generatorTypes.sampleID,
47
+ this.samples.indexOf(z.sample),
48
+ false
49
+ ));
50
+ return z.generators.length * GEN_BYTE_SIZE + sum;
53
51
  }, 0);
54
52
  }
55
53
  const igendata = new IndexedByteArray(igensize);
56
- let igenIndex = 0;
54
+
55
+ /**
56
+ * @param z {BasicZone}
57
+ */
58
+ const writeZone = z =>
59
+ {
60
+ for (const gen of z.generators)
61
+ {
62
+ // name is deceptive, it works on negatives
63
+ writeWord(igendata, gen.generatorType);
64
+ writeWord(igendata, gen.generatorValue);
65
+ }
66
+ };
67
+
57
68
  for (const instrument of this.instruments)
58
69
  {
70
+ // global zone
71
+ writeZone(instrument.globalZone);
59
72
  for (const instrumentZone of instrument.instrumentZones)
60
73
  {
61
- // set the start index here
62
- instrumentZone.generatorZoneStartIndex = igenIndex;
63
- for (const gen of instrumentZone.generators)
64
- {
65
- // name is deceptive, it works on negatives
66
- writeWord(igendata, gen.generatorType);
67
- writeWord(igendata, gen.generatorValue);
68
- igenIndex++;
69
- }
74
+ writeZone(instrumentZone);
70
75
  }
71
76
  }
72
77
  // terminal generator, is zero
73
78
  writeDword(igendata, 0);
74
-
75
79
  return writeRIFFChunk(new RiffChunk(
76
80
  "igen",
77
81
  igendata.length,
@@ -1,6 +1,7 @@
1
1
  import { IndexedByteArray } from "../../../utils/indexed_array.js";
2
2
  import { writeLittleEndian, writeWord } from "../../../utils/byte_functions/little_endian.js";
3
3
  import { RiffChunk, writeRIFFChunk } from "../riff_chunk.js";
4
+ import { MOD_BYTE_SIZE } from "../modulator.js";
4
5
 
5
6
  /**
6
7
  * @this {BasicSoundBank}
@@ -10,33 +11,42 @@ export function getIMOD()
10
11
  {
11
12
  // very similar to igen,
12
13
  // go through all instruments -> zones and write modulators sequentially
13
- let imodsize = 10;
14
+ let imodsize = MOD_BYTE_SIZE; // terminal
14
15
  for (const inst of this.instruments)
15
16
  {
16
- imodsize += inst.instrumentZones.reduce((sum, z) => z.modulators.length * 10 + sum, 0);
17
+ imodsize += inst.globalZone.modulators.length * MOD_BYTE_SIZE;
18
+ // start with one mod for global
19
+ imodsize += inst.instrumentZones.reduce((sum, z) => z.modulators.length * MOD_BYTE_SIZE + sum, 0);
17
20
  }
18
21
  const imoddata = new IndexedByteArray(imodsize);
19
- let imodIndex = 0;
22
+
23
+ /**
24
+ * @param z {BasicZone}
25
+ */
26
+ const writeZone = z =>
27
+ {
28
+ for (const mod of z.modulators)
29
+ {
30
+ writeWord(imoddata, mod.getSourceEnum());
31
+ writeWord(imoddata, mod.modulatorDestination);
32
+ writeWord(imoddata, mod.transformAmount);
33
+ writeWord(imoddata, mod.getSecSrcEnum());
34
+ writeWord(imoddata, mod.transformType);
35
+ }
36
+ };
37
+
20
38
  for (const inst of this.instruments)
21
39
  {
22
- for (const ibag of inst.instrumentZones)
40
+ // global
41
+ writeZone(inst.globalZone);
42
+ for (const instrumentZone of inst.instrumentZones)
23
43
  {
24
- // set the start index here
25
- ibag.modulatorZoneStartIndex = imodIndex;
26
- for (const mod of ibag.modulators)
27
- {
28
- writeWord(imoddata, mod.getSourceEnum());
29
- writeWord(imoddata, mod.modulatorDestination);
30
- writeWord(imoddata, mod.transformAmount);
31
- writeWord(imoddata, mod.getSecSrcEnum());
32
- writeWord(imoddata, mod.transformType);
33
- imodIndex++;
34
- }
44
+ writeZone(instrumentZone);
35
45
  }
36
46
  }
37
47
 
38
48
  // terminal modulator, is zero
39
- writeLittleEndian(imoddata, 0, 10);
49
+ writeLittleEndian(imoddata, 0, MOD_BYTE_SIZE);
40
50
 
41
51
  return writeRIFFChunk(new RiffChunk(
42
52
  "imod",
@@ -3,24 +3,23 @@ import { writeStringAsBytes } from "../../../utils/byte_functions/string.js";
3
3
  import { writeWord } from "../../../utils/byte_functions/little_endian.js";
4
4
  import { RiffChunk, writeRIFFChunk } from "../riff_chunk.js";
5
5
 
6
+ const INST_SIZE = 22;
7
+
6
8
  /**
7
9
  * @this {BasicSoundBank}
8
10
  * @returns {IndexedByteArray}
9
11
  */
10
12
  export function getINST()
11
13
  {
12
- const instsize = this.instruments.length * 22 + 22;
14
+ const instsize = this.instruments.length * INST_SIZE + INST_SIZE;
13
15
  const instdata = new IndexedByteArray(instsize);
14
16
  // the instrument start index is adjusted in ibag, write it here
15
17
  let instrumentStart = 0;
16
- let instrumentID = 0;
17
18
  for (const inst of this.instruments)
18
19
  {
19
20
  writeStringAsBytes(instdata, inst.instrumentName, 20);
20
21
  writeWord(instdata, instrumentStart);
21
- instrumentStart += inst.instrumentZones.length;
22
- inst.instrumentID = instrumentID;
23
- instrumentID++;
22
+ instrumentStart += inst.instrumentZones.length + 1; // global
24
23
  }
25
24
  // write EOI
26
25
  writeStringAsBytes(instdata, "EOI", 20);
@@ -2,6 +2,8 @@ import { IndexedByteArray } from "../../../utils/indexed_array.js";
2
2
  import { writeWord } from "../../../utils/byte_functions/little_endian.js";
3
3
  import { RiffChunk, writeRIFFChunk } from "../riff_chunk.js";
4
4
 
5
+ const BAG_SIZE = 4;
6
+
5
7
  /**
6
8
  * @this {BasicSoundBank}
7
9
  * @returns {IndexedByteArray}
@@ -9,30 +11,31 @@ import { RiffChunk, writeRIFFChunk } from "../riff_chunk.js";
9
11
  export function getPBAG()
10
12
  {
11
13
  // write all pbag with their start indexes as they were changed in getPGEN() and getPMOD()
12
- const pbagsize = this.presets.reduce((sum, i) => i.presetZones.length * 4 + sum, 4);
14
+ const pbagsize = this.presets.reduce((sum, i) =>
15
+ // +1 because global zone
16
+ (i.presetZones.length + 1) * BAG_SIZE + sum, BAG_SIZE);
13
17
  const pbagdata = new IndexedByteArray(pbagsize);
14
- let zoneID = 0;
15
18
  let generatorIndex = 0;
16
19
  let modulatorIndex = 0;
20
+
21
+ /**
22
+ * @param z {BasicZone}
23
+ */
24
+ const writeZone = z =>
25
+ {
26
+ writeWord(pbagdata, generatorIndex);
27
+ writeWord(pbagdata, modulatorIndex);
28
+ generatorIndex += z.generators.length;
29
+ modulatorIndex += z.modulators.length;
30
+ };
31
+
17
32
  for (const preset of this.presets)
18
33
  {
19
- // ensure that the first zone is global
20
- const zones = preset.presetZones.filter(z => !z.isGlobal);
21
- const global = preset.presetZones.filter(z => z.isGlobal);
22
- // only take the first one
23
- if (global?.[0])
24
- {
25
- zones.unshift(global?.[0]);
26
- }
27
- preset.presetZoneStartIndex = zoneID;
28
- for (const pbag of zones)
34
+ // global
35
+ writeZone(preset.globalZone);
36
+ for (const pbag of preset.presetZones)
29
37
  {
30
- pbag.zoneID = zoneID;
31
- writeWord(pbagdata, generatorIndex);
32
- writeWord(pbagdata, modulatorIndex);
33
- generatorIndex += pbag.generators.length;
34
- modulatorIndex += pbag.modulators.length;
35
- zoneID++;
38
+ writeZone(pbag);
36
39
  }
37
40
  }
38
41
  // write the terminal PBAG
@@ -1,8 +1,9 @@
1
- import { writeWord } from "../../../utils/byte_functions/little_endian.js";
1
+ import { writeDword, writeWord } from "../../../utils/byte_functions/little_endian.js";
2
2
  import { IndexedByteArray } from "../../../utils/indexed_array.js";
3
3
  import { RiffChunk, writeRIFFChunk } from "../riff_chunk.js";
4
4
 
5
- import { Generator, generatorTypes } from "../generator.js";
5
+ import { GEN_BYTE_SIZE, Generator } from "../generator.js";
6
+ import { generatorTypes } from "../generator_types.js";
6
7
 
7
8
  /**
8
9
  * @this {BasicSoundBank}
@@ -12,9 +13,10 @@ export function getPGEN()
12
13
  {
13
14
  // almost identical to igen, except the correct instrument instead of sample gen
14
15
  // goes through all preset zones and writes generators sequentially (add 4 for terminal)
15
- let pgensize = 4;
16
+ let pgensize = GEN_BYTE_SIZE;
16
17
  for (const preset of this.presets)
17
18
  {
19
+ pgensize += preset.globalZone.generators.length * GEN_BYTE_SIZE;
18
20
  pgensize += preset.presetZones.reduce((size, z) =>
19
21
  {
20
22
  // clear instrument and range generators before determining the size
@@ -26,7 +28,7 @@ export function getPGEN()
26
28
  // unshift vel then key and instrument is last
27
29
  if (z.hasVelRange)
28
30
  {
29
- z.generators.unshift(new Generator(
31
+ z.prependGenerator(new Generator(
30
32
  generatorTypes.velRange,
31
33
  z.velRange.max << 8 | Math.max(z.velRange.min, 0),
32
34
  false
@@ -34,45 +36,46 @@ export function getPGEN()
34
36
  }
35
37
  if (z.hasKeyRange)
36
38
  {
37
- z.generators.unshift(new Generator(
39
+ z.prependGenerator(new Generator(
38
40
  generatorTypes.keyRange,
39
41
  z.keyRange.max << 8 | Math.max(z.keyRange.min, 0),
40
42
  false
41
43
  ));
42
44
  }
43
- if (!z.isGlobal)
44
- {
45
- // write the instrument
46
- z.generators.push(new Generator(
47
- generatorTypes.instrument,
48
- this.instruments.indexOf(z.instrument),
49
- false
50
- ));
51
- }
52
- return z.generators.length * 4 + size;
45
+ // write the instrument id
46
+ z.addGenerators(new Generator(
47
+ generatorTypes.instrument,
48
+ this.instruments.indexOf(z.instrument),
49
+ false
50
+ ));
51
+ return z.generators.length * GEN_BYTE_SIZE + size;
53
52
  }, 0);
54
53
  }
55
54
  const pgendata = new IndexedByteArray(pgensize);
56
- let pgenIndex = 0;
55
+
56
+ /**
57
+ * @param z {BasicZone}
58
+ */
59
+ const writeZone = z =>
60
+ {
61
+ for (const gen of z.generators)
62
+ {
63
+ // name is deceptive, it works on negatives
64
+ writeWord(pgendata, gen.generatorType);
65
+ writeWord(pgendata, gen.generatorValue);
66
+ }
67
+ };
57
68
  for (const preset of this.presets)
58
69
  {
59
- for (const presetZone of preset.presetZones)
70
+ // global zone
71
+ writeZone(preset.globalZone);
72
+ for (const zone of preset.presetZones)
60
73
  {
61
- // set the start index here
62
- presetZone.generatorZoneStartIndex = pgenIndex;
63
- // write generators
64
- for (const gen of presetZone.generators)
65
- {
66
- // name is deceptive, it works on negatives
67
- writeWord(pgendata, gen.generatorType);
68
- writeWord(pgendata, gen.generatorValue);
69
- }
70
- pgenIndex += presetZone.generators.length;
74
+ writeZone(zone);
71
75
  }
72
76
  }
73
77
  // terminal generator, is zero
74
- writeWord(pgendata, 0);
75
- writeWord(pgendata, 0);
78
+ writeDword(pgendata, 0);
76
79
 
77
80
  return writeRIFFChunk(new RiffChunk(
78
81
  "pgen",
@@ -3,13 +3,15 @@ import { writeStringAsBytes } from "../../../utils/byte_functions/string.js";
3
3
  import { writeDword, writeWord } from "../../../utils/byte_functions/little_endian.js";
4
4
  import { RiffChunk, writeRIFFChunk } from "../riff_chunk.js";
5
5
 
6
+ const PHDR_SIZE = 38;
7
+
6
8
  /**
7
9
  * @this {BasicSoundBank}
8
10
  * @returns {IndexedByteArray}
9
11
  */
10
12
  export function getPHDR()
11
13
  {
12
- const phdrsize = this.presets.length * 38 + 38;
14
+ const phdrsize = this.presets.length * PHDR_SIZE + PHDR_SIZE;
13
15
  const phdrdata = new IndexedByteArray(phdrsize);
14
16
  // the preset start is adjusted in pbag, this is only for the terminal preset index
15
17
  let presetStart = 0;
@@ -23,7 +25,7 @@ export function getPHDR()
23
25
  writeDword(phdrdata, preset.library);
24
26
  writeDword(phdrdata, preset.genre);
25
27
  writeDword(phdrdata, preset.morphology);
26
- presetStart += preset.presetZones.length;
28
+ presetStart += preset.presetZones.length + 1; // global
27
29
  }
28
30
  // write EOP
29
31
  writeStringAsBytes(phdrdata, "EOP", 20);
@@ -1,6 +1,7 @@
1
1
  import { IndexedByteArray } from "../../../utils/indexed_array.js";
2
2
  import { writeLittleEndian, writeWord } from "../../../utils/byte_functions/little_endian.js";
3
3
  import { RiffChunk, writeRIFFChunk } from "../riff_chunk.js";
4
+ import { MOD_BYTE_SIZE } from "../modulator.js";
4
5
 
5
6
  /**
6
7
  * @this {BasicSoundBank}
@@ -10,33 +11,42 @@ export function getPMOD()
10
11
  {
11
12
  // very similar to imod,
12
13
  // go through all presets -> zones and write modulators sequentially
13
- let pmodsize = 10;
14
+ let pmodsize = MOD_BYTE_SIZE;
14
15
  for (const preset of this.presets)
15
16
  {
16
- pmodsize += preset.presetZones.reduce((sum, z) => z.modulators.length * 10 + sum, 0);
17
+ pmodsize += preset.globalZone.modulators.length * MOD_BYTE_SIZE;
18
+ pmodsize += preset.presetZones.reduce((sum, z) => z.modulators.length * MOD_BYTE_SIZE + sum, 0);
17
19
  }
18
20
  const pmoddata = new IndexedByteArray(pmodsize);
19
- let pmodIndex = 0;
21
+
22
+ /**
23
+ * @param z {BasicZone}
24
+ */
25
+ const writeZone = z =>
26
+ {
27
+ for (const mod of z.modulators)
28
+ {
29
+ writeWord(pmoddata, mod.getSourceEnum());
30
+ writeWord(pmoddata, mod.modulatorDestination);
31
+ writeWord(pmoddata, mod.transformAmount);
32
+ writeWord(pmoddata, mod.getSecSrcEnum());
33
+ writeWord(pmoddata, mod.transformType);
34
+ }
35
+ };
36
+
37
+
20
38
  for (const preset of this.presets)
21
39
  {
22
- for (const pbag of preset.presetZones)
40
+ // global
41
+ writeZone(preset.globalZone);
42
+ for (const zone of preset.presetZones)
23
43
  {
24
- // set the start index here
25
- pbag.modulatorZoneStartIndex = pmodIndex;
26
- for (const mod of pbag.modulators)
27
- {
28
- writeWord(pmoddata, mod.getSourceEnum());
29
- writeWord(pmoddata, mod.modulatorDestination);
30
- writeWord(pmoddata, mod.transformAmount);
31
- writeWord(pmoddata, mod.getSecSrcEnum());
32
- writeWord(pmoddata, mod.transformType);
33
- pmodIndex++;
34
- }
44
+ writeZone(zone);
35
45
  }
36
46
  }
37
47
 
38
48
  // terminal modulator, is zero
39
- writeLittleEndian(pmoddata, 0, 10);
49
+ writeLittleEndian(pmoddata, 0, MOD_BYTE_SIZE);
40
50
 
41
51
  return writeRIFFChunk(new RiffChunk(
42
52
  "pmod",
@@ -14,12 +14,16 @@ import { getPBAG } from "./pbag.js";
14
14
  import { getPHDR } from "./phdr.js";
15
15
  import { writeLittleEndian, writeWord } from "../../../utils/byte_functions/little_endian.js";
16
16
  import { SpessaSynthGroupCollapsed, SpessaSynthGroupEnd, SpessaSynthInfo } from "../../../utils/loggin.js";
17
+ import { MOD_BYTE_SIZE } from "../modulator.js";
18
+ import { fillWithDefaults } from "../../../utils/fill_with_defaults.js";
17
19
  /**
18
20
  * @typedef {Object} SoundFont2WriteOptions
19
- * @property {boolean} compress - if the soundfont should be compressed with the Ogg Vorbis codec
20
- * @property {number} compressionQuality - the vorbis compression quality, from -0.1 to 1
21
- * @property {EncodeVorbisFunction|undefined} compressionFunction - the encode vorbis function.
22
- * Can be undefined if not compressed.
21
+ * @property {boolean|undefined} compress - if the soundfont should be compressed with the Ogg Vorbis codec
22
+ * @property {number|undefined} compressionQuality - the vorbis compression quality, from -0.1 to 1
23
+ * @property {EncodeVorbisFunction|undefined} compressionFunction -
24
+ * the encode vorbis function. Can be undefined if not compressed.
25
+ * @property {boolean|undefined} writeDefaultModulators - if the DMOD chunk should be written.
26
+ * Recommended.
23
27
  */
24
28
 
25
29
  /**
@@ -28,7 +32,8 @@ import { SpessaSynthGroupCollapsed, SpessaSynthGroupEnd, SpessaSynthInfo } from
28
32
  const DEFAULT_WRITE_OPTIONS = {
29
33
  compress: false,
30
34
  compressionQuality: 0.5,
31
- compressionFunction: undefined
35
+ compressionFunction: undefined,
36
+ writeDefaultModulators: true
32
37
  };
33
38
 
34
39
  /**
@@ -39,6 +44,7 @@ const DEFAULT_WRITE_OPTIONS = {
39
44
  */
40
45
  export function write(options = DEFAULT_WRITE_OPTIONS)
41
46
  {
47
+ options = fillWithDefaults(options, DEFAULT_WRITE_OPTIONS);
42
48
  if (options.compress)
43
49
  {
44
50
  if (typeof options.compressionFunction !== "function")
@@ -72,7 +78,7 @@ export function write(options = DEFAULT_WRITE_OPTIONS)
72
78
  this.soundFontInfo["ifil"] = "3.0"; // set version to 3
73
79
  }
74
80
 
75
- if (this.defaultModulators.length > 0)
81
+ if (options?.writeDefaultModulators)
76
82
  {
77
83
  // trigger the DMOD write
78
84
  this.soundFontInfo["DMOD"] = `${this.defaultModulators.length} Modulators`;
@@ -108,7 +114,7 @@ export function write(options = DEFAULT_WRITE_OPTIONS)
108
114
  consoleColors.recognized,
109
115
  consoleColors.info
110
116
  );
111
- let dmodsize = 10 + mods.length * 10;
117
+ let dmodsize = MOD_BYTE_SIZE + mods.length * MOD_BYTE_SIZE;
112
118
  const dmoddata = new IndexedByteArray(dmodsize);
113
119
  for (const mod of mods)
114
120
  {
@@ -120,7 +126,7 @@ export function write(options = DEFAULT_WRITE_OPTIONS)
120
126
  }
121
127
 
122
128
  // terminal modulator, is zero
123
- writeLittleEndian(dmoddata, 0, 10);
129
+ writeLittleEndian(dmoddata, 0, MOD_BYTE_SIZE);
124
130
 
125
131
  infoArrays.push(writeRIFFChunk(new RiffChunk(
126
132
  type,
@@ -1,11 +1,17 @@
1
1
  import { DLSSources } from "./dls_sources.js";
2
- import { getModSourceEnum, Modulator, modulatorCurveTypes, modulatorSources } from "../basic_soundfont/modulator.js";
2
+ import {
3
+ DecodedModulator,
4
+ getModSourceEnum,
5
+ Modulator,
6
+ modulatorCurveTypes,
7
+ modulatorSources
8
+ } from "../basic_soundfont/modulator.js";
3
9
  import { midiControllers } from "../../midi/midi_message.js";
4
10
  import { DLSDestinations } from "./dls_destinations.js";
5
11
 
6
- import { generatorTypes } from "../basic_soundfont/generator.js";
7
12
  import { consoleColors } from "../../utils/other.js";
8
13
  import { SpessaSynthWarn } from "../../utils/loggin.js";
14
+ import { generatorTypes } from "../basic_soundfont/generator_types.js";
9
15
 
10
16
  /**
11
17
  * @param source {number}
@@ -385,7 +391,7 @@ export function getSF2ModulatorFromArticulator(
385
391
  }
386
392
 
387
393
  // return the modulator!
388
- return new Modulator(
394
+ return new DecodedModulator(
389
395
  sourceEnumFinal,
390
396
  secSourceEnumFinal,
391
397
  destinationGenerator,
@@ -1,5 +1,5 @@
1
1
  import { BasicPreset } from "../basic_soundfont/basic_preset.js";
2
- import { BasicPresetZone } from "../basic_soundfont/basic_zones.js";
2
+ import { BasicPresetZone } from "../basic_soundfont/basic_preset_zone.js";
3
3
  import { BasicInstrument } from "../basic_soundfont/basic_instrument.js";
4
4
 
5
5
  export class DLSPreset extends BasicPreset
@@ -34,10 +34,9 @@ export class DLSPreset extends BasicPreset
34
34
  }
35
35
 
36
36
  this.DLSInstrument = new BasicInstrument();
37
- this.DLSInstrument.addUseCount();
38
37
 
39
38
  const zone = new BasicPresetZone();
40
- zone.instrument = this.DLSInstrument;
39
+ zone.setInstrument(this.DLSInstrument);
41
40
 
42
41
  this.presetZones = [zone];
43
42
  }