spessasynth_lib 3.20.24 → 3.20.26

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.
@@ -1,4 +1,8 @@
1
1
  export class BasicPreset {
2
+ /**
3
+ * @param modulators {Modulator[]}
4
+ */
5
+ constructor(modulators: Modulator[]);
2
6
  /**
3
7
  * The preset's name
4
8
  * @type {string}
@@ -44,6 +48,11 @@ export class BasicPreset {
44
48
  * @type {number}
45
49
  */
46
50
  morphology: number;
51
+ /**
52
+ * Default modulators
53
+ * @type {Modulator[]}
54
+ */
55
+ defaultModulators: Modulator[];
47
56
  deletePreset(): void;
48
57
  /**
49
58
  * @param index {number}
@@ -74,3 +83,4 @@ export type SampleAndGenerators = {
74
83
  sample: BasicSample;
75
84
  sampleID: number;
76
85
  };
86
+ import { Modulator } from '../read_sf2/modulators.js';
@@ -17,10 +17,10 @@ export class BasicSoundFont {
17
17
  });
18
18
  /**
19
19
  * Soundfont's info stored as name: value. ifil and iver are stored as string representation of float (e.g. 2.1)
20
- * @type {Object<string, string>}
20
+ * @type {Object<string, string|IndexedByteArray>}
21
21
  */
22
22
  soundFontInfo: {
23
- [x: string]: string;
23
+ [x: string]: string | IndexedByteArray;
24
24
  };
25
25
  /**
26
26
  * The soundfont's presets
@@ -37,6 +37,11 @@ export class BasicSoundFont {
37
37
  * @type {BasicInstrument[]}
38
38
  */
39
39
  instruments: BasicInstrument[];
40
+ /**
41
+ * Soundfont's default modulatorss
42
+ * @type {Modulator[]}
43
+ */
44
+ defaultModulators: Modulator[];
40
45
  removeUnusedElements(): void;
41
46
  /**
42
47
  * @param instrument {BasicInstrument}
@@ -78,4 +83,5 @@ export class BasicSoundFont {
78
83
  getPresetByName(presetName: string): BasicPreset;
79
84
  write: typeof write;
80
85
  }
86
+ import { Modulator } from '../read_sf2/modulators.js';
81
87
  import { write } from './write_sf2/write.js';
@@ -29,6 +29,17 @@ export namespace modulatorCurveTypes {
29
29
  */
30
30
  export const precomputedTransforms: Float32Array[][][];
31
31
  export class Modulator {
32
+ /**
33
+ * @param modulator {Modulator}
34
+ * @returns {Modulator}
35
+ */
36
+ static copy(modulator: Modulator): Modulator;
37
+ /**
38
+ * @param mod1 {Modulator}
39
+ * @param mod2 {Modulator}
40
+ * @returns {boolean}
41
+ */
42
+ static isIdentical(mod1: Modulator, mod2: Modulator): boolean;
32
43
  /**
33
44
  * Creates a modulator
34
45
  * @param dataArray {IndexedByteArray|{srcEnum: number, secSrcEnum: number, dest:number, amt: number, transform: number}}
@@ -40,12 +51,12 @@ export class Modulator {
40
51
  amt: number;
41
52
  transform: number;
42
53
  });
43
- modulatorSource: any;
54
+ sourceEnum: any;
44
55
  /**
45
56
  * @type {generatorTypes}
46
57
  */
47
58
  modulatorDestination: generatorTypes;
48
- modulationSecondarySrc: any;
59
+ secondarySourceEnum: any;
49
60
  transformAmount: any;
50
61
  transformType: any;
51
62
  sourcePolarity: number;
@@ -2,9 +2,10 @@
2
2
  * Reads the presets
3
3
  * @param presetChunk {RiffChunk}
4
4
  * @param presetZones {PresetZone[]}
5
+ * @param defaultModulators {Modulator[]}
5
6
  * @returns {Preset[]}
6
7
  */
7
- export function readPresets(presetChunk: RiffChunk, presetZones: PresetZone[]): Preset[];
8
+ export function readPresets(presetChunk: RiffChunk, presetZones: PresetZone[], defaultModulators: Modulator[]): Preset[];
8
9
  /**
9
10
  * parses soundfont presets, also includes function for getting the generators and samples from midi note and velocity
10
11
  */
@@ -12,8 +13,9 @@ export class Preset extends BasicPreset {
12
13
  /**
13
14
  * Creates a preset
14
15
  * @param presetChunk {RiffChunk}
16
+ * @param defaultModulators {Modulator[]}
15
17
  */
16
- constructor(presetChunk: RiffChunk);
18
+ constructor(presetChunk: RiffChunk, defaultModulators: Modulator[]);
17
19
  presetZoneStartIndex: number;
18
20
  presetZonesAmount: number;
19
21
  /**
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "spessasynth_lib",
3
- "version": "3.20.24",
4
- "description": "MIDI and SoundFont2 or DLS Synthesizer library with no compromises",
3
+ "version": "3.20.26",
4
+ "description": "MIDI and SoundFont2/DLS library with no compromises",
5
5
  "browser": "index.js",
6
6
  "types": "@types/index.d.ts",
7
7
  "type": "module",
@@ -7,12 +7,15 @@
7
7
  * sampleID: number,
8
8
  * }} SampleAndGenerators
9
9
  */
10
- import { defaultModulators } from '../read_sf2/modulators.js'
11
10
  import { generatorTypes } from '../read_sf2/generators.js'
11
+ import { Modulator } from '../read_sf2/modulators.js'
12
12
 
13
13
  export class BasicPreset
14
14
  {
15
- constructor()
15
+ /**
16
+ * @param modulators {Modulator[]}
17
+ */
18
+ constructor(modulators)
16
19
  {
17
20
  /**
18
21
  * The preset's name
@@ -67,6 +70,12 @@ export class BasicPreset
67
70
  * @type {number}
68
71
  */
69
72
  this.morphology = 0;
73
+
74
+ /**
75
+ * Default modulators
76
+ * @type {Modulator[]}
77
+ */
78
+ this.defaultModulators = modulators;
70
79
  }
71
80
 
72
81
  deletePreset()
@@ -137,21 +146,9 @@ export class BasicPreset
137
146
  return [];
138
147
  }
139
148
 
140
- function isInRange(min, max, number) {
141
- return number >= min && number <= max;
142
- }
143
-
144
- /**
145
- * @param mod1 {Modulator}
146
- * @param mod2 {Modulator}
147
- * @returns {boolean}
148
- */
149
- function identicalMod(mod1, mod2)
149
+ function isInRange(min, max, number)
150
150
  {
151
- return (mod1.modulatorSource === mod2.modulatorSource)
152
- && (mod1.modulatorDestination === mod2.modulatorDestination)
153
- && (mod1.modulationSecondarySrc === mod2.modulationSecondarySrc)
154
- && (mod1.transformType === mod2.transformType);
151
+ return number >= min && number <= max;
155
152
  }
156
153
 
157
154
  /**
@@ -169,7 +166,7 @@ export class BasicPreset
169
166
  */
170
167
  function addUniqueMods(main, adder)
171
168
  {
172
- main.push(...adder.filter(m => !main.find(mm => identicalMod(m, mm))));
169
+ main.push(...adder.filter(m => !main.find(mm => Modulator.isIdentical(m, mm))));
173
170
  }
174
171
 
175
172
  /**
@@ -237,7 +234,7 @@ export class BasicPreset
237
234
  addUniqueMods(instrumentModulators, globalInstrumentModulators);
238
235
 
239
236
  // default mods
240
- addUniqueMods(instrumentModulators, defaultModulators);
237
+ addUniqueMods(instrumentModulators, this.defaultModulators);
241
238
 
242
239
  /**
243
240
  * sum preset modulators to instruments (amount) sf spec page 54
@@ -247,7 +244,7 @@ export class BasicPreset
247
244
  for(let i = 0; i < presetModulators.length; i++)
248
245
  {
249
246
  let mod = presetModulators[i];
250
- const identicalInstrumentModulator = finalModulatorList.findIndex(m => identicalMod(mod, m));
247
+ const identicalInstrumentModulator = finalModulatorList.findIndex(m => Modulator.isIdentical(mod, m));
251
248
  if(identicalInstrumentModulator !== -1)
252
249
  {
253
250
  // sum the amounts (this makes a new modulator because otherwise it would overwrite the one in the soundfont!!!
@@ -1,6 +1,7 @@
1
1
  import { SpessaSynthWarn } from '../../utils/loggin.js'
2
2
  import { consoleColors } from '../../utils/other.js'
3
3
  import { write } from './write_sf2/write.js'
4
+ import { defaultModulators, Modulator } from '../read_sf2/modulators.js'
4
5
 
5
6
  class BasicSoundFont
6
7
  {
@@ -12,7 +13,7 @@ class BasicSoundFont
12
13
  {
13
14
  /**
14
15
  * Soundfont's info stored as name: value. ifil and iver are stored as string representation of float (e.g. 2.1)
15
- * @type {Object<string, string>}
16
+ * @type {Object<string, string|IndexedByteArray>}
16
17
  */
17
18
  this.soundFontInfo = {};
18
19
 
@@ -34,6 +35,12 @@ class BasicSoundFont
34
35
  */
35
36
  this.instruments = [];
36
37
 
38
+ /**
39
+ * Soundfont's default modulatorss
40
+ * @type {Modulator[]}
41
+ */
42
+ this.defaultModulators = defaultModulators.map(m => Modulator.copy(m));
43
+
37
44
  if(data?.presets)
38
45
  {
39
46
  this.presets.push(...data.presets);
@@ -133,18 +140,25 @@ class BasicSoundFont
133
140
  */
134
141
  getPreset(bankNr, programNr)
135
142
  {
143
+ // check for exact match
136
144
  let preset = this.presets.find(p => p.bank === bankNr && p.program === programNr);
137
145
  if (!preset)
138
146
  {
139
- preset = this.presets.find(p => p.program === programNr && p.bank !== 128);
147
+ // no match...
140
148
  if(bankNr === 128)
141
149
  {
150
+ // drum preset: find any preset with bank 128
142
151
  preset = this.presets.find(p => p.bank === 128 && p.program === programNr);
143
152
  if(!preset)
144
153
  {
145
154
  preset = this.presets.find(p => p.bank === 128);
146
155
  }
147
156
  }
157
+ else
158
+ {
159
+ // non drum preset: find any preset with the given program that is not a drum preset
160
+ preset = this.presets.find(p => p.program === programNr && p.bank !== 128);
161
+ }
148
162
  if(preset)
149
163
  {
150
164
  SpessaSynthWarn(`%cPreset ${bankNr}.${programNr} not found. Replaced with %c${preset.presetName} (${preset.bank}.${preset.program})`,
@@ -152,6 +166,7 @@ class BasicSoundFont
152
166
  consoleColors.recognized);
153
167
  }
154
168
  }
169
+ // no preset, use the first one available
155
170
  if(!preset)
156
171
  {
157
172
  SpessaSynthWarn(`Preset ${programNr} not found. Defaulting to`, this.presets[0].presetName);
@@ -25,10 +25,10 @@ export function getIMOD()
25
25
  ibag.modulatorZoneStartIndex = imodIndex;
26
26
  for (const mod of ibag.modulators)
27
27
  {
28
- writeWord(imoddata, mod.modulatorSource);
28
+ writeWord(imoddata, mod.sourceEnum);
29
29
  writeWord(imoddata, mod.modulatorDestination);
30
30
  writeWord(imoddata, mod.transformAmount);
31
- writeWord(imoddata, mod.modulationSecondarySrc);
31
+ writeWord(imoddata, mod.secondarySourceEnum);
32
32
  writeWord(imoddata, mod.transformType);
33
33
  imodIndex++;
34
34
  }
@@ -25,10 +25,10 @@ export function getPMOD()
25
25
  pbag.modulatorZoneStartIndex = pmodIndex;
26
26
  for (const mod of pbag.modulators)
27
27
  {
28
- writeWord(pmoddata, mod.modulatorSource);
28
+ writeWord(pmoddata, mod.sourceEnum);
29
29
  writeWord(pmoddata, mod.modulatorDestination);
30
30
  writeWord(pmoddata, mod.transformAmount);
31
- writeWord(pmoddata, mod.modulationSecondarySrc);
31
+ writeWord(pmoddata, mod.secondarySourceEnum);
32
32
  writeWord(pmoddata, mod.transformType);
33
33
  pmodIndex++;
34
34
  }
@@ -68,6 +68,7 @@ export function write(options = DEFAULT_WRITE_OPTIONS)
68
68
  {
69
69
  this.soundFontInfo["ifil"] = "3.0"; // set version to 3
70
70
  }
71
+
71
72
  for (const [type, data] of Object.entries(this.soundFontInfo))
72
73
  {
73
74
  if(type === "ifil" || type === "iver")
@@ -84,6 +85,15 @@ export function write(options = DEFAULT_WRITE_OPTIONS)
84
85
  )));
85
86
  }
86
87
  else
88
+ if(type === "DMOD")
89
+ {
90
+ infoArrays.push(writeRIFFChunk(new RiffChunk(
91
+ type,
92
+ data.length,
93
+ data
94
+ )));
95
+ }
96
+ else
87
97
  {
88
98
  const arr = new IndexedByteArray(data.length);
89
99
  writeStringAsBytes(arr, data);
@@ -1,6 +1,7 @@
1
1
  import { BasicPreset } from '../basic_soundfont/basic_preset.js'
2
2
  import { BasicPresetZone } from '../basic_soundfont/basic_zones.js'
3
3
  import { BasicInstrument } from '../basic_soundfont/basic_instrument.js'
4
+ import { defaultModulators } from '../read_sf2/modulators.js'
4
5
 
5
6
  export class DLSPreset extends BasicPreset
6
7
  {
@@ -11,7 +12,8 @@ export class DLSPreset extends BasicPreset
11
12
  */
12
13
  constructor(ulBank, ulInstrument)
13
14
  {
14
- super();
15
+ // use stock default modulators, dls won't ever have DMOD chunk
16
+ super(defaultModulators);
15
17
  this.program = ulInstrument & 127;
16
18
  this.bank = (ulBank >> 8) & 127;
17
19
  const isDrums = ulBank >> 31;
@@ -48,6 +48,8 @@ class DLSSoundFont extends BasicSoundFont
48
48
  this.soundFontInfo["isng"] = "EMU8000";
49
49
 
50
50
  // set some defaults
51
+ this.soundFontInfo["INAM"] = "Unnamed DLS";
52
+ this.soundFontInfo["IENG"] = "Unknown";
51
53
  this.soundFontInfo["IPRD"] = "SpessaSynth DLS";
52
54
  this.soundFontInfo["ICRD"] = new Date().toDateString();
53
55
 
@@ -61,7 +63,7 @@ class DLSSoundFont extends BasicSoundFont
61
63
  this.soundFontInfo[infoPart.header] = readBytesAsString(infoPart.chunkData, infoPart.size);
62
64
  }
63
65
  }
64
- this.soundFontInfo["ICMT"] = (this.soundFontInfo["ICMT"] || "") + "\nConverted from DLS to SF2 with SpessaSynth";
66
+ this.soundFontInfo["ICMT"] = (this.soundFontInfo["ICMT"] || "(No description)") + "\nConverted from DLS to SF2 with SpessaSynth";
65
67
  if(this.soundFontInfo["ISBJ"])
66
68
  {
67
69
  // merge it
@@ -101,6 +103,9 @@ class DLSSoundFont extends BasicSoundFont
101
103
  }
102
104
  this.readDLSInstrumentList(instrumentListChunk);
103
105
 
106
+ // sort presets
107
+ this.presets.sort((a, b) => (a.program - b.program) + (a.bank - b.bank));
108
+
104
109
  SpessaSynthInfo(`%cParsing finished! %c"${this.soundFontInfo["INAM"] || "UNNAMED"}"%c has %c${this.presets.length} %cpresets,
105
110
  %c${this.instruments.length}%c instruments and %c${this.samples.length}%c samples.`,
106
111
  consoleColors.info,
@@ -42,6 +42,7 @@ export class DLSZone extends BasicInstrumentZone
42
42
  this.isGlobal = false;
43
43
 
44
44
  // correct tuning if needed
45
+ samplePitchCorrection -= sample.samplePitchCorrection;
45
46
  const coarseTune = Math.trunc(samplePitchCorrection / 100);
46
47
  if(coarseTune !== 0)
47
48
  {
@@ -44,21 +44,21 @@ export class Modulator{
44
44
  constructor(dataArray) {
45
45
  if(dataArray.srcEnum)
46
46
  {
47
- this.modulatorSource = dataArray.srcEnum;
47
+ this.sourceEnum = dataArray.srcEnum;
48
48
  /**
49
49
  * @type {generatorTypes}
50
50
  */
51
51
  this.modulatorDestination = dataArray.dest;
52
- this.modulationSecondarySrc = dataArray.secSrcEnum;
52
+ this.secondarySourceEnum = dataArray.secSrcEnum;
53
53
  this.transformAmount = dataArray.amt;
54
54
  this.transformType = dataArray.transform;
55
55
  }
56
56
  else
57
57
  {
58
- this.modulatorSource = readLittleEndian(dataArray, 2);
58
+ this.sourceEnum = readLittleEndian(dataArray, 2);
59
59
  this.modulatorDestination = readLittleEndian(dataArray, 2);
60
60
  this.transformAmount = signedInt16(dataArray[dataArray.currentIndex++], dataArray[dataArray.currentIndex++]);
61
- this.modulationSecondarySrc = readLittleEndian(dataArray, 2);
61
+ this.secondarySourceEnum = readLittleEndian(dataArray, 2);
62
62
  this.transformType = readLittleEndian(dataArray, 2);
63
63
  }
64
64
 
@@ -68,22 +68,50 @@ export class Modulator{
68
68
  }
69
69
 
70
70
  // decode the source
71
- this.sourcePolarity = this.modulatorSource >> 9 & 1;
72
- this.sourceDirection = this.modulatorSource >> 8 & 1;
73
- this.sourceUsesCC = this.modulatorSource >> 7 & 1;
74
- this.sourceIndex = this.modulatorSource & 127;
75
- this.sourceCurveType = this.modulatorSource >> 10 & 3;
71
+ this.sourcePolarity = this.sourceEnum >> 9 & 1;
72
+ this.sourceDirection = this.sourceEnum >> 8 & 1;
73
+ this.sourceUsesCC = this.sourceEnum >> 7 & 1;
74
+ this.sourceIndex = this.sourceEnum & 127;
75
+ this.sourceCurveType = this.sourceEnum >> 10 & 3;
76
76
 
77
77
  // decode the secondary source
78
- this.secSrcPolarity = this.modulationSecondarySrc >> 9 & 1;
79
- this.secSrcDirection = this.modulationSecondarySrc >> 8 & 1;
80
- this.secSrcUsesCC = this.modulationSecondarySrc >> 7 & 1;
81
- this.secSrcIndex = this.modulationSecondarySrc & 127;
82
- this.secSrcCurveType = this.modulationSecondarySrc >> 10 & 3;
78
+ this.secSrcPolarity = this.secondarySourceEnum >> 9 & 1;
79
+ this.secSrcDirection = this.secondarySourceEnum >> 8 & 1;
80
+ this.secSrcUsesCC = this.secondarySourceEnum >> 7 & 1;
81
+ this.secSrcIndex = this.secondarySourceEnum & 127;
82
+ this.secSrcCurveType = this.secondarySourceEnum >> 10 & 3;
83
83
 
84
84
  //this.precomputeModulatorTransform();
85
85
  }
86
86
 
87
+ /**
88
+ * @param modulator {Modulator}
89
+ * @returns {Modulator}
90
+ */
91
+ static copy(modulator)
92
+ {
93
+ return new Modulator({
94
+ srcEnum: modulator.sourceEnum,
95
+ secSrcEnum: modulator.secondarySourceEnum,
96
+ transform: modulator.transformType,
97
+ amt: modulator.transformAmount,
98
+ dest: modulator.modulatorDestination
99
+ });
100
+ }
101
+
102
+ /**
103
+ * @param mod1 {Modulator}
104
+ * @param mod2 {Modulator}
105
+ * @returns {boolean}
106
+ */
107
+ static isIdentical(mod1, mod2)
108
+ {
109
+ return (mod1.sourceEnum === mod2.sourceEnum)
110
+ && (mod1.modulatorDestination === mod2.modulatorDestination)
111
+ && (mod1.secondarySourceEnum === mod2.secondarySourceEnum)
112
+ && (mod1.transformType === mod2.transformType);
113
+ }
114
+
87
115
  /**
88
116
  * Sums transform and creates a NEW modulator
89
117
  * @param modulator {Modulator}
@@ -92,8 +120,8 @@ export class Modulator{
92
120
  sumTransform(modulator)
93
121
  {
94
122
  return new Modulator({
95
- srcEnum: this.modulatorSource,
96
- secSrcEnum: this.modulationSecondarySrc,
123
+ srcEnum: this.sourceEnum,
124
+ secSrcEnum: this.secondarySourceEnum,
97
125
  dest: this.modulatorDestination,
98
126
  transform: this.transformType,
99
127
  amt: this.transformAmount + modulator.transformAmount
@@ -190,10 +218,10 @@ export const defaultModulators = [
190
218
  }),
191
219
 
192
220
  // reverb effects to send
193
- new Modulator({srcEnum: 0x00DB, dest: generatorTypes.reverbEffectsSend, amt: 200, secSrcEnum: 0x0, transform: 0}),
221
+ new Modulator({srcEnum: 0x00DB, dest: generatorTypes.reverbEffectsSend, amt: 750, secSrcEnum: 0x0, transform: 0}),
194
222
 
195
223
  // chorus effects to send
196
- new Modulator({srcEnum: 0x00DD, dest: generatorTypes.chorusEffectsSend, amt: 200, secSrcEnum: 0x0, transform: 0}),
224
+ new Modulator({srcEnum: 0x00DD, dest: generatorTypes.chorusEffectsSend, amt: 750, secSrcEnum: 0x0, transform: 0}),
197
225
 
198
226
  // custom modulators heck yeah
199
227
  // poly pressure to vibrato
@@ -13,10 +13,11 @@ export class Preset extends BasicPreset
13
13
  /**
14
14
  * Creates a preset
15
15
  * @param presetChunk {RiffChunk}
16
+ * @param defaultModulators {Modulator[]}
16
17
  */
17
- constructor(presetChunk)
18
+ constructor(presetChunk, defaultModulators)
18
19
  {
19
- super();
20
+ super(defaultModulators);
20
21
  this.presetName = readBytesAsString(presetChunk.chunkData, 20)
21
22
  .trim()
22
23
  .replace(/\d{3}:\d{3}/, ""); // remove those pesky "000:001"
@@ -51,9 +52,10 @@ export class Preset extends BasicPreset
51
52
  * Reads the presets
52
53
  * @param presetChunk {RiffChunk}
53
54
  * @param presetZones {PresetZone[]}
55
+ * @param defaultModulators {Modulator[]}
54
56
  * @returns {Preset[]}
55
57
  */
56
- export function readPresets(presetChunk, presetZones)
58
+ export function readPresets(presetChunk, presetZones, defaultModulators)
57
59
  {
58
60
  /**
59
61
  * @type {Preset[]}
@@ -61,7 +63,7 @@ export function readPresets(presetChunk, presetZones)
61
63
  let presets = [];
62
64
  while(presetChunk.chunkData.length > presetChunk.chunkData.currentIndex)
63
65
  {
64
- let preset = new Preset(presetChunk);
66
+ let preset = new Preset(presetChunk, defaultModulators);
65
67
  if(presets.length > 0)
66
68
  {
67
69
  let presetZonesAmount = preset.presetZoneStartIndex - presets[presets.length - 1].presetZoneStartIndex;
@@ -66,26 +66,41 @@ export class SoundFont2 extends BasicSoundFont
66
66
  {
67
67
  let chunk = readRIFFChunk(infoChunk.chunkData);
68
68
  let text;
69
- // special case: ifil
69
+ // special cases
70
70
  switch (chunk.header.toLowerCase())
71
71
  {
72
72
  case "ifil":
73
73
  case "iver":
74
74
  text = `${readLittleEndian(chunk.chunkData, 2)}.${readLittleEndian(chunk.chunkData, 2)}`;
75
+ this.soundFontInfo[chunk.header] = text;
75
76
  break;
76
77
 
77
78
  case "icmt":
78
79
  text = readBytesAsString(chunk.chunkData, chunk.chunkData.length, undefined, false);
80
+ this.soundFontInfo[chunk.header] = text;
81
+ break;
82
+
83
+ // dmod: default modulators
84
+ case "dmod":
85
+ const newModulators = readModulators(chunk);
86
+ newModulators.pop(); // remove the terminal record
87
+ text = `Modulators: ${newModulators.length}`;
88
+ // override default modulators
89
+ const oldDefaults = this.defaultModulators;
90
+
91
+ this.defaultModulators = newModulators;
92
+ this.defaultModulators.push(...oldDefaults.filter(m => !this.defaultModulators.find(mm => Modulator.isIdentical(m, mm))));
93
+ this.soundFontInfo[chunk.header] = chunk.chunkData;
79
94
  break;
80
95
 
81
96
  default:
82
97
  text = readBytesAsString(chunk.chunkData, chunk.chunkData.length);
98
+ this.soundFontInfo[chunk.header] = text;
83
99
  }
84
100
 
85
101
  SpessaSynthInfo(`%c"${chunk.header}": %c"${text}"`,
86
102
  consoleColors.info,
87
103
  consoleColors.recognized);
88
- this.soundFontInfo[chunk.header] = text;
89
104
  }
90
105
 
91
106
  // SDTA
@@ -214,7 +229,7 @@ export class SoundFont2 extends BasicSoundFont
214
229
 
215
230
  let presetZones = readPresetZones(presetZonesChunk, presetGenerators, presetModulators, this.instruments);
216
231
 
217
- this.presets.push(...readPresets(presetHeadersChunk, presetZones));
232
+ this.presets.push(...readPresets(presetHeadersChunk, presetZones, this.defaultModulators));
218
233
  this.presets.sort((a, b) => (a.program - b.program) + (a.bank - b.bank));
219
234
  // preload the first preset
220
235
  SpessaSynthInfo(`%cParsing finished! %c"${this.soundFontInfo["INAM"]}"%c has %c${this.presets.length} %cpresets,