spessasynth_lib 3.20.23 → 3.20.25

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';
@@ -13,10 +13,11 @@ export class DLSZone extends BasicInstrumentZone {
13
13
  * @param sampleKey {number}
14
14
  * @param sample {BasicSample}
15
15
  * @param sampleID {number}
16
+ * @param samplePitchCorrection {number} cents
16
17
  */
17
18
  setWavesample(attenuationCb: number, loopingMode: number, loop: {
18
19
  start: number;
19
20
  end: number;
20
- }, sampleKey: number, sample: BasicSample, sampleID: number): void;
21
+ }, sampleKey: number, sample: BasicSample, sampleID: number, samplePitchCorrection: number): void;
21
22
  }
22
23
  import { BasicInstrumentZone } from '../basic_soundfont/basic_zones.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,6 +1,6 @@
1
1
  {
2
2
  "name": "spessasynth_lib",
3
- "version": "3.20.23",
3
+ "version": "3.20.25",
4
4
  "description": "MIDI and SoundFont2 or DLS Synthesizer library with no compromises",
5
5
  "browser": "index.js",
6
6
  "types": "@types/index.d.ts",
@@ -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);
@@ -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;
@@ -22,6 +22,7 @@ export class DLSZone extends BasicInstrumentZone
22
22
  * @param sampleKey {number}
23
23
  * @param sample {BasicSample}
24
24
  * @param sampleID {number}
25
+ * @param samplePitchCorrection {number} cents
25
26
  */
26
27
  setWavesample(
27
28
  attenuationCb,
@@ -30,6 +31,7 @@ export class DLSZone extends BasicInstrumentZone
30
31
  sampleKey,
31
32
  sample,
32
33
  sampleID,
34
+ samplePitchCorrection,
33
35
  )
34
36
  {
35
37
  if(loopingMode !== 0)
@@ -39,6 +41,18 @@ export class DLSZone extends BasicInstrumentZone
39
41
  this.generators.push(new Generator(generatorTypes.initialAttenuation, attenuationCb));
40
42
  this.isGlobal = false;
41
43
 
44
+ // correct tuning if needed
45
+ const coarseTune = Math.trunc(samplePitchCorrection / 100);
46
+ if(coarseTune !== 0)
47
+ {
48
+ this.generators.push(new Generator(generatorTypes.coarseTune, coarseTune));
49
+ }
50
+ const fineTune = samplePitchCorrection - (coarseTune * 100);
51
+ if(fineTune !== 0)
52
+ {
53
+ this.generators.push(new Generator(generatorTypes.fineTune, fineTune));
54
+ }
55
+
42
56
  // correct loop if needed
43
57
  const diffStart = loop.start - (sample.sampleLoopStartIndex / 2);
44
58
  const diffEnd = loop.end - (sample.sampleLoopEndIndex / 2);
@@ -1,4 +1,4 @@
1
- import { readLittleEndian } from '../../utils/byte_functions/little_endian.js'
1
+ import { readLittleEndian, signedInt16 } from '../../utils/byte_functions/little_endian.js'
2
2
  import { findRIFFListType, readRIFFChunk } from '../basic_soundfont/riff_chunk.js'
3
3
  import { Generator, generatorTypes } from '../read_sf2/generators.js'
4
4
  import { DLSZone } from './dls_zone.js'
@@ -57,8 +57,12 @@ export function readRegion(chunk)
57
57
  // cbSize
58
58
  readLittleEndian(waveSampleChunk.chunkData, 4);
59
59
  const originalKey = readLittleEndian(waveSampleChunk.chunkData, 2);
60
- // pitch correction is read from the wave wsmpl chunk
61
- readLittleEndian(waveSampleChunk.chunkData, 2);
60
+
61
+ // sFineTune
62
+ const pitchCorrection = signedInt16(
63
+ waveSampleChunk.chunkData[waveSampleChunk.chunkData.currentIndex++],
64
+ waveSampleChunk.chunkData[waveSampleChunk.chunkData.currentIndex++]
65
+ );
62
66
 
63
67
  // gain correction: Each unit of gain represents 1/655360 dB
64
68
  const gainCorrection = readLittleEndian(waveSampleChunk.chunkData, 4);
@@ -124,6 +128,7 @@ export function readRegion(chunk)
124
128
  loop,
125
129
  originalKey,
126
130
  sample,
127
- sampleID);
131
+ sampleID,
132
+ pitchCorrection);
128
133
  return zone;
129
134
  }
@@ -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,