spessasynth_core 3.26.18 → 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.
- package/package.json +1 -1
- package/src/soundfont/basic_soundfont/basic_instrument.js +4 -4
- package/src/soundfont/basic_soundfont/basic_preset.js +8 -0
- package/src/soundfont/basic_soundfont/basic_soundbank.js +76 -13
- package/src/soundfont/basic_soundfont/basic_zone.js +72 -0
- package/src/soundfont/basic_soundfont/generator.js +2 -11
- package/src/soundfont/basic_soundfont/write_dls/combine_zones.js +3 -3
- package/src/soundfont/basic_soundfont/write_sf2/igen.js +5 -5
- package/src/soundfont/basic_soundfont/write_sf2/imod.js +2 -2
- package/src/soundfont/basic_soundfont/write_sf2/pgen.js +3 -3
- package/src/soundfont/basic_soundfont/write_sf2/pmod.js +1 -1
- package/src/soundfont/basic_soundfont/write_sf2/write.js +14 -8
- package/src/soundfont/dls/dls_soundfont.js +1 -2
- package/src/soundfont/dls/dls_zone.js +9 -11
- package/src/soundfont/dls/read_instrument.js +5 -5
- package/src/soundfont/dls/read_lart.js +4 -4
- package/src/soundfont/dls/read_region.js +1 -2
- package/src/soundfont/read_sf2/instruments.js +1 -1
- package/src/soundfont/read_sf2/soundfont.js +2 -3
- package/src/soundfont/read_sf2/zones.js +4 -60
- package/src/synthetizer/audio_engine/engine_components/compute_modulator.js +19 -18
- package/src/synthetizer/audio_engine/engine_methods/controller_control/reset_controllers.js +0 -4
package/package.json
CHANGED
|
@@ -37,12 +37,12 @@ export class BasicInstrument
|
|
|
37
37
|
}
|
|
38
38
|
|
|
39
39
|
/**
|
|
40
|
-
* @param
|
|
40
|
+
* @param zones {BasicInstrumentZone}
|
|
41
41
|
*/
|
|
42
|
-
|
|
42
|
+
addZones(...zones)
|
|
43
43
|
{
|
|
44
|
-
|
|
45
|
-
this.instrumentZones.push(
|
|
44
|
+
zones.forEach(z => z.useCount++);
|
|
45
|
+
this.instrumentZones.push(...zones);
|
|
46
46
|
}
|
|
47
47
|
|
|
48
48
|
addUseCount()
|
|
@@ -75,6 +75,14 @@ export class BasicPreset
|
|
|
75
75
|
this.parentSoundBank = parentSoundBank;
|
|
76
76
|
}
|
|
77
77
|
|
|
78
|
+
/**
|
|
79
|
+
* @param zones {BasicPresetZone}
|
|
80
|
+
*/
|
|
81
|
+
addZones(...zones)
|
|
82
|
+
{
|
|
83
|
+
this.presetZones.push(...zones);
|
|
84
|
+
}
|
|
85
|
+
|
|
78
86
|
/**
|
|
79
87
|
* @param allowXG {boolean}
|
|
80
88
|
* @param allowSFX {boolean}
|
|
@@ -67,15 +67,47 @@ class BasicSoundBank
|
|
|
67
67
|
isXGBank = false;
|
|
68
68
|
|
|
69
69
|
/**
|
|
70
|
-
* Creates a new basic soundfont template
|
|
70
|
+
* Creates a new basic soundfont template (or copies)
|
|
71
71
|
* @param data {undefined|{presets: BasicPreset[], info: Object<string, string>}}
|
|
72
72
|
*/
|
|
73
73
|
constructor(data = undefined)
|
|
74
74
|
{
|
|
75
75
|
if (data?.presets)
|
|
76
76
|
{
|
|
77
|
-
this.presets.push(...data.presets);
|
|
78
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);
|
|
79
111
|
}
|
|
80
112
|
}
|
|
81
113
|
|
|
@@ -127,43 +159,74 @@ class BasicSoundBank
|
|
|
127
159
|
{
|
|
128
160
|
sample.sampleData[i] = (i / 128) * 2 - 1;
|
|
129
161
|
}
|
|
130
|
-
font.
|
|
162
|
+
font.addSamples(sample);
|
|
131
163
|
|
|
132
164
|
const gZone = new BasicGlobalZone();
|
|
133
|
-
gZone.
|
|
134
|
-
|
|
135
|
-
|
|
165
|
+
gZone.addGenerators(
|
|
166
|
+
new Generator(generatorTypes.initialAttenuation, 375),
|
|
167
|
+
new Generator(generatorTypes.releaseVolEnv, -1000),
|
|
168
|
+
new Generator(generatorTypes.sampleModes, 1)
|
|
169
|
+
);
|
|
136
170
|
|
|
137
171
|
const zone1 = new BasicInstrumentZone();
|
|
138
172
|
zone1.setSample(sample);
|
|
139
173
|
|
|
140
174
|
const zone2 = new BasicInstrumentZone();
|
|
141
175
|
zone2.setSample(sample);
|
|
142
|
-
zone2.
|
|
176
|
+
zone2.addGenerators(new Generator(generatorTypes.fineTune, -9));
|
|
143
177
|
|
|
144
178
|
|
|
145
179
|
const inst = new BasicInstrument();
|
|
146
180
|
inst.instrumentName = "Saw Wave";
|
|
147
181
|
inst.globalZone = gZone;
|
|
148
|
-
inst.
|
|
149
|
-
|
|
150
|
-
font.instruments.push(inst);
|
|
182
|
+
inst.addZones(zone1, zone2);
|
|
183
|
+
font.addInstruments(inst);
|
|
151
184
|
|
|
152
185
|
const pZone = new BasicPresetZone();
|
|
153
186
|
pZone.setInstrument(inst);
|
|
154
187
|
|
|
155
188
|
const preset = new BasicPreset(font);
|
|
156
189
|
preset.presetName = "Saw Wave";
|
|
157
|
-
preset.
|
|
158
|
-
font.
|
|
190
|
+
preset.addZones(pZone);
|
|
191
|
+
font.addPresets(preset);
|
|
159
192
|
|
|
160
193
|
font.soundFontInfo["ifil"] = "2.1";
|
|
161
194
|
font.soundFontInfo["isng"] = "EMU8000";
|
|
162
195
|
font.soundFontInfo["INAM"] = "Dummy";
|
|
163
|
-
font.
|
|
196
|
+
font.flush();
|
|
164
197
|
return font.write().buffer;
|
|
165
198
|
}
|
|
166
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
|
+
|
|
167
230
|
/**
|
|
168
231
|
* parses the bank after loading is done
|
|
169
232
|
* @protected
|
|
@@ -3,6 +3,8 @@
|
|
|
3
3
|
* @property {number} min - the minimum midi note
|
|
4
4
|
* @property {number} max - the maximum midi note
|
|
5
5
|
*/
|
|
6
|
+
import { generatorTypes } from "./generator_types.js";
|
|
7
|
+
import { Generator } from "./generator.js";
|
|
6
8
|
|
|
7
9
|
export class BasicZone
|
|
8
10
|
{
|
|
@@ -47,6 +49,76 @@ export class BasicZone
|
|
|
47
49
|
return this.velRange.min !== -1;
|
|
48
50
|
}
|
|
49
51
|
|
|
52
|
+
/**
|
|
53
|
+
* Adds at the start
|
|
54
|
+
* @param generator {Generator}
|
|
55
|
+
*/
|
|
56
|
+
prependGenerator(generator)
|
|
57
|
+
{
|
|
58
|
+
this.generators.unshift(generator);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* @param type {generatorTypes}
|
|
63
|
+
* @param value {number}
|
|
64
|
+
*/
|
|
65
|
+
setGenerator(type, value)
|
|
66
|
+
{
|
|
67
|
+
switch (type)
|
|
68
|
+
{
|
|
69
|
+
case generatorTypes.sampleID:
|
|
70
|
+
throw new Error("Use setSample()");
|
|
71
|
+
case generatorTypes.instrument:
|
|
72
|
+
throw new Error("Use setInstrument()");
|
|
73
|
+
|
|
74
|
+
case generatorTypes.velRange:
|
|
75
|
+
case generatorTypes.keyRange:
|
|
76
|
+
throw new Error("Set the range manually");
|
|
77
|
+
}
|
|
78
|
+
let generator = this.generators.find(g => g.generatorType === type);
|
|
79
|
+
if (generator)
|
|
80
|
+
{
|
|
81
|
+
generator.generatorValue = value;
|
|
82
|
+
}
|
|
83
|
+
else
|
|
84
|
+
{
|
|
85
|
+
this.addGenerators(new Generator(type, value));
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* @param generators {Generator}
|
|
91
|
+
*/
|
|
92
|
+
addGenerators(...generators)
|
|
93
|
+
{
|
|
94
|
+
generators.forEach(g =>
|
|
95
|
+
{
|
|
96
|
+
switch (g.generatorType)
|
|
97
|
+
{
|
|
98
|
+
default:
|
|
99
|
+
this.generators.push(g);
|
|
100
|
+
break;
|
|
101
|
+
|
|
102
|
+
case generatorTypes.velRange:
|
|
103
|
+
this.velRange.min = g.generatorValue & 0x7F;
|
|
104
|
+
this.velRange.max = (g.generatorValue >> 8) & 0x7F;
|
|
105
|
+
break;
|
|
106
|
+
|
|
107
|
+
case generatorTypes.keyRange:
|
|
108
|
+
this.keyRange.min = g.generatorValue & 0x7F;
|
|
109
|
+
this.keyRange.max = (g.generatorValue >> 8) & 0x7F;
|
|
110
|
+
}
|
|
111
|
+
});
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* @param modulators {Modulator}
|
|
116
|
+
*/
|
|
117
|
+
addModulators(...modulators)
|
|
118
|
+
{
|
|
119
|
+
this.modulators.push(...modulators);
|
|
120
|
+
}
|
|
121
|
+
|
|
50
122
|
/**
|
|
51
123
|
* @param generatorType {generatorTypes}
|
|
52
124
|
* @param notFoundValue {number}
|
|
@@ -68,15 +68,6 @@ export function addAndClampGenerator(generatorType, presetGens, instrumentGens)
|
|
|
68
68
|
instruValue = instruGen.generatorValue;
|
|
69
69
|
}
|
|
70
70
|
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
// Special case, initial attenuation.
|
|
74
|
-
// Shall get clamped in the volume envelope,
|
|
75
|
-
// so the modulators can be affected by negative generators (the "Brass" patch was problematic...)
|
|
76
|
-
if (generatorType === generatorTypes.initialAttenuation)
|
|
77
|
-
{
|
|
78
|
-
return value;
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
return Math.max(limits.min, Math.min(limits.max, value));
|
|
71
|
+
// limits are applied in the compute_modulator function
|
|
72
|
+
return instruValue + presetValue;
|
|
82
73
|
}
|
|
@@ -308,7 +308,7 @@ export function combineZones(preset, globalize = true)
|
|
|
308
308
|
// if the global value is the default value just remove it, no need to add it
|
|
309
309
|
if (targetValue !== defaultForChecked)
|
|
310
310
|
{
|
|
311
|
-
globalZone.
|
|
311
|
+
globalZone.addGenerators(new Generator(checkedType, targetValue));
|
|
312
312
|
}
|
|
313
313
|
// remove from the zones
|
|
314
314
|
finalZones.forEach(z =>
|
|
@@ -329,7 +329,7 @@ export function combineZones(preset, globalize = true)
|
|
|
329
329
|
// Since we're globalizing, we need to add the default here.
|
|
330
330
|
if (targetValue !== defaultForChecked)
|
|
331
331
|
{
|
|
332
|
-
z.
|
|
332
|
+
z.addGenerators(new Generator(checkedType, defaultForChecked));
|
|
333
333
|
}
|
|
334
334
|
}
|
|
335
335
|
});
|
|
@@ -360,7 +360,7 @@ export function combineZones(preset, globalize = true)
|
|
|
360
360
|
}
|
|
361
361
|
if (existsForAllZones === true)
|
|
362
362
|
{
|
|
363
|
-
globalZone.
|
|
363
|
+
globalZone.addModulators(Modulator.copy(checkedModulator));
|
|
364
364
|
// delete it from local zones.
|
|
365
365
|
for (const zone of finalZones)
|
|
366
366
|
{
|
|
@@ -18,16 +18,16 @@ export function getIGEN()
|
|
|
18
18
|
igensize += inst.instrumentZones.reduce((sum, z) =>
|
|
19
19
|
{
|
|
20
20
|
// clear sample and range generators before determining the size
|
|
21
|
-
z.generators = z.generators.filter(g =>
|
|
21
|
+
z.generators = (z.generators.filter(g =>
|
|
22
22
|
g.generatorType !== generatorTypes.sampleID &&
|
|
23
23
|
g.generatorType !== generatorTypes.keyRange &&
|
|
24
24
|
g.generatorType !== generatorTypes.velRange
|
|
25
|
-
);
|
|
25
|
+
));
|
|
26
26
|
// add sample and ranges if necessary
|
|
27
27
|
// unshift vel then key (to make key first) and the sample is last
|
|
28
28
|
if (z.hasVelRange)
|
|
29
29
|
{
|
|
30
|
-
z.
|
|
30
|
+
z.prependGenerator(new Generator(
|
|
31
31
|
generatorTypes.velRange,
|
|
32
32
|
z.velRange.max << 8 | Math.max(z.velRange.min, 0),
|
|
33
33
|
false
|
|
@@ -35,14 +35,14 @@ export function getIGEN()
|
|
|
35
35
|
}
|
|
36
36
|
if (z.hasKeyRange)
|
|
37
37
|
{
|
|
38
|
-
z.
|
|
38
|
+
z.prependGenerator(new Generator(
|
|
39
39
|
generatorTypes.keyRange,
|
|
40
40
|
z.keyRange.max << 8 | Math.max(z.keyRange.min, 0),
|
|
41
41
|
false
|
|
42
42
|
));
|
|
43
43
|
}
|
|
44
44
|
// add sample id
|
|
45
|
-
z.
|
|
45
|
+
z.addGenerators(new Generator(
|
|
46
46
|
generatorTypes.sampleID,
|
|
47
47
|
this.samples.indexOf(z.sample),
|
|
48
48
|
false
|
|
@@ -16,7 +16,7 @@ export function getIMOD()
|
|
|
16
16
|
{
|
|
17
17
|
imodsize += inst.globalZone.modulators.length * MOD_BYTE_SIZE;
|
|
18
18
|
// start with one mod for global
|
|
19
|
-
imodsize += inst.instrumentZones.reduce((sum, z) => z.modulators.length *
|
|
19
|
+
imodsize += inst.instrumentZones.reduce((sum, z) => z.modulators.length * MOD_BYTE_SIZE + sum, 0);
|
|
20
20
|
}
|
|
21
21
|
const imoddata = new IndexedByteArray(imodsize);
|
|
22
22
|
|
|
@@ -46,7 +46,7 @@ export function getIMOD()
|
|
|
46
46
|
}
|
|
47
47
|
|
|
48
48
|
// terminal modulator, is zero
|
|
49
|
-
writeLittleEndian(imoddata, 0,
|
|
49
|
+
writeLittleEndian(imoddata, 0, MOD_BYTE_SIZE);
|
|
50
50
|
|
|
51
51
|
return writeRIFFChunk(new RiffChunk(
|
|
52
52
|
"imod",
|
|
@@ -28,7 +28,7 @@ export function getPGEN()
|
|
|
28
28
|
// unshift vel then key and instrument is last
|
|
29
29
|
if (z.hasVelRange)
|
|
30
30
|
{
|
|
31
|
-
z.
|
|
31
|
+
z.prependGenerator(new Generator(
|
|
32
32
|
generatorTypes.velRange,
|
|
33
33
|
z.velRange.max << 8 | Math.max(z.velRange.min, 0),
|
|
34
34
|
false
|
|
@@ -36,14 +36,14 @@ export function getPGEN()
|
|
|
36
36
|
}
|
|
37
37
|
if (z.hasKeyRange)
|
|
38
38
|
{
|
|
39
|
-
z.
|
|
39
|
+
z.prependGenerator(new Generator(
|
|
40
40
|
generatorTypes.keyRange,
|
|
41
41
|
z.keyRange.max << 8 | Math.max(z.keyRange.min, 0),
|
|
42
42
|
false
|
|
43
43
|
));
|
|
44
44
|
}
|
|
45
45
|
// write the instrument id
|
|
46
|
-
z.
|
|
46
|
+
z.addGenerators(new Generator(
|
|
47
47
|
generatorTypes.instrument,
|
|
48
48
|
this.instruments.indexOf(z.instrument),
|
|
49
49
|
false
|
|
@@ -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 -
|
|
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 (
|
|
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 =
|
|
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,
|
|
129
|
+
writeLittleEndian(dmoddata, 0, MOD_BYTE_SIZE);
|
|
124
130
|
|
|
125
131
|
infoArrays.push(writeRIFFChunk(new RiffChunk(
|
|
126
132
|
type,
|
|
@@ -114,8 +114,7 @@ class DLSSoundFont extends BasicSoundBank
|
|
|
114
114
|
this.readDLSInstrumentList(instrumentListChunk);
|
|
115
115
|
|
|
116
116
|
// sort presets
|
|
117
|
-
this.
|
|
118
|
-
this._parseInternal();
|
|
117
|
+
this.flush();
|
|
119
118
|
SpessaSynthInfo(
|
|
120
119
|
`%cParsing finished! %c"${this.soundFontInfo["INAM"] || "UNNAMED"}"%c has %c${this.presets.length} %cpresets,
|
|
121
120
|
%c${this.instruments.length}%c instruments and %c${this.samples.length}%c samples.`,
|
|
@@ -36,22 +36,21 @@ export class DLSZone extends BasicInstrumentZone
|
|
|
36
36
|
{
|
|
37
37
|
if (loopingMode !== 0)
|
|
38
38
|
{
|
|
39
|
-
this.
|
|
39
|
+
this.addGenerators(new Generator(generatorTypes.sampleModes, loopingMode));
|
|
40
40
|
}
|
|
41
|
-
this.
|
|
42
|
-
this.isGlobal = false;
|
|
41
|
+
this.addGenerators(new Generator(generatorTypes.initialAttenuation, attenuationCb));
|
|
43
42
|
|
|
44
43
|
// correct tuning if needed
|
|
45
44
|
samplePitchCorrection -= sample.samplePitchCorrection;
|
|
46
45
|
const coarseTune = Math.trunc(samplePitchCorrection / 100);
|
|
47
46
|
if (coarseTune !== 0)
|
|
48
47
|
{
|
|
49
|
-
this.
|
|
48
|
+
this.addGenerators(new Generator(generatorTypes.coarseTune, coarseTune));
|
|
50
49
|
}
|
|
51
50
|
const fineTune = samplePitchCorrection - (coarseTune * 100);
|
|
52
51
|
if (fineTune !== 0)
|
|
53
52
|
{
|
|
54
|
-
this.
|
|
53
|
+
this.addGenerators(new Generator(generatorTypes.fineTune, fineTune));
|
|
55
54
|
}
|
|
56
55
|
|
|
57
56
|
// correct loop if needed
|
|
@@ -62,33 +61,32 @@ export class DLSZone extends BasicInstrumentZone
|
|
|
62
61
|
if (diffStart !== 0)
|
|
63
62
|
{
|
|
64
63
|
const fine = diffStart % 32768;
|
|
65
|
-
this.
|
|
64
|
+
this.addGenerators(new Generator(generatorTypes.startloopAddrsOffset, fine));
|
|
66
65
|
// coarse generator uses 32768 samples per step
|
|
67
66
|
const coarse = Math.trunc(diffStart / 32768);
|
|
68
67
|
if (coarse !== 0)
|
|
69
68
|
{
|
|
70
|
-
this.
|
|
69
|
+
this.addGenerators(new Generator(generatorTypes.startloopAddrsCoarseOffset, coarse));
|
|
71
70
|
}
|
|
72
71
|
}
|
|
73
72
|
if (diffEnd !== 0)
|
|
74
73
|
{
|
|
75
74
|
const fine = diffEnd % 32768;
|
|
76
|
-
this.
|
|
75
|
+
this.addGenerators(new Generator(generatorTypes.endloopAddrsOffset, fine));
|
|
77
76
|
// coarse generator uses 32768 samples per step
|
|
78
77
|
const coarse = Math.trunc(diffEnd / 32768);
|
|
79
78
|
if (coarse !== 0)
|
|
80
79
|
{
|
|
81
|
-
this.
|
|
80
|
+
this.addGenerators(new Generator(generatorTypes.endloopAddrsCoarseOffset, coarse));
|
|
82
81
|
}
|
|
83
82
|
}
|
|
84
83
|
}
|
|
85
84
|
// correct the key if needed
|
|
86
85
|
if (sampleKey !== sample.samplePitch)
|
|
87
86
|
{
|
|
88
|
-
this.
|
|
87
|
+
this.addGenerators(new Generator(generatorTypes.overridingRootKey, sampleKey));
|
|
89
88
|
}
|
|
90
89
|
// add sample ID
|
|
91
|
-
this.generators.push(new Generator(generatorTypes.sampleID, sampleID));
|
|
92
90
|
this.setSample(sample);
|
|
93
91
|
}
|
|
94
92
|
}
|
|
@@ -84,12 +84,12 @@ export function readDLSInstrument(chunk)
|
|
|
84
84
|
// reverb
|
|
85
85
|
if (globalZone.modulators.find(m => m.modulatorDestination === generatorTypes.reverbEffectsSend) === undefined)
|
|
86
86
|
{
|
|
87
|
-
globalZone.
|
|
87
|
+
globalZone.addModulators(Modulator.copy(DEFAULT_DLS_REVERB));
|
|
88
88
|
}
|
|
89
89
|
// chorus
|
|
90
90
|
if (globalZone.modulators.find(m => m.modulatorDestination === generatorTypes.chorusEffectsSend) === undefined)
|
|
91
91
|
{
|
|
92
|
-
globalZone.
|
|
92
|
+
globalZone.addModulators(Modulator.copy(DEFAULT_DLS_CHORUS));
|
|
93
93
|
}
|
|
94
94
|
|
|
95
95
|
// read regions
|
|
@@ -108,11 +108,11 @@ export function readDLSInstrument(chunk)
|
|
|
108
108
|
const zone = this.readRegion(chunk);
|
|
109
109
|
if (zone)
|
|
110
110
|
{
|
|
111
|
-
preset.DLSInstrument.
|
|
111
|
+
preset.DLSInstrument.addZones(zone);
|
|
112
112
|
}
|
|
113
113
|
}
|
|
114
114
|
|
|
115
|
-
this.
|
|
116
|
-
this.
|
|
115
|
+
this.addPresets(preset);
|
|
116
|
+
this.addInstruments(preset.DLSInstrument);
|
|
117
117
|
SpessaSynthGroupEnd();
|
|
118
118
|
}
|
|
@@ -16,8 +16,8 @@ export function readLart(lartChunk, lar2Chunk, zone)
|
|
|
16
16
|
const art1 = readRIFFChunk(lartChunk.chunkData);
|
|
17
17
|
this.verifyHeader(art1, "art1", "art2");
|
|
18
18
|
const modsAndGens = readArticulation(art1, true);
|
|
19
|
-
zone.
|
|
20
|
-
zone.
|
|
19
|
+
zone.addGenerators(...modsAndGens.generators);
|
|
20
|
+
zone.addModulators(...modsAndGens.modulators);
|
|
21
21
|
}
|
|
22
22
|
}
|
|
23
23
|
|
|
@@ -28,8 +28,8 @@ export function readLart(lartChunk, lar2Chunk, zone)
|
|
|
28
28
|
const art2 = readRIFFChunk(lar2Chunk.chunkData);
|
|
29
29
|
this.verifyHeader(art2, "art2", "art1");
|
|
30
30
|
const modsAndGens = readArticulation(art2, false);
|
|
31
|
-
zone.
|
|
32
|
-
zone.
|
|
31
|
+
zone.addGenerators(...modsAndGens.generators);
|
|
32
|
+
zone.addModulators(...modsAndGens.modulators);
|
|
33
33
|
}
|
|
34
34
|
}
|
|
35
35
|
}
|
|
@@ -52,7 +52,7 @@ export function readRegion(chunk)
|
|
|
52
52
|
const exclusive = readLittleEndian(regionHeader.chunkData, 2);
|
|
53
53
|
if (exclusive !== 0)
|
|
54
54
|
{
|
|
55
|
-
zone.
|
|
55
|
+
zone.addGenerators(new Generator(generatorTypes.exclusiveClass, exclusive));
|
|
56
56
|
}
|
|
57
57
|
|
|
58
58
|
// lart
|
|
@@ -61,7 +61,6 @@ export function readRegion(chunk)
|
|
|
61
61
|
this.readLart(lart, lar2, zone);
|
|
62
62
|
|
|
63
63
|
// wsmp: wave sample chunk
|
|
64
|
-
zone.isGlobal = false;
|
|
65
64
|
const waveSampleChunk = regionChunks.find(c => c.header === "wsmp");
|
|
66
65
|
// cbSize
|
|
67
66
|
readLittleEndian(waveSampleChunk.chunkData, 4);
|
|
@@ -242,9 +242,8 @@ export class SoundFont2 extends BasicSoundBank
|
|
|
242
242
|
|
|
243
243
|
let presetZones = readPresetZones(presetZonesChunk, presetGenerators, presetModulators, this.instruments);
|
|
244
244
|
|
|
245
|
-
this.
|
|
246
|
-
this.
|
|
247
|
-
this._parseInternal();
|
|
245
|
+
this.addPresets(...readPresets(presetHeadersChunk, presetZones, this));
|
|
246
|
+
this.flush();
|
|
248
247
|
SpessaSynthInfo(
|
|
249
248
|
`%cParsing finished! %c"${this.soundFontInfo["INAM"]}"%c has %c${this.presets.length} %cpresets,
|
|
250
249
|
%c${this.instruments.length}%c instruments and %c${this.samples.length}%c samples.`,
|
|
@@ -46,7 +46,7 @@ export class InstrumentZone extends BasicInstrumentZone
|
|
|
46
46
|
{
|
|
47
47
|
throw new Error("Missing generator in instrument zone! The file may corrupted.");
|
|
48
48
|
}
|
|
49
|
-
this.
|
|
49
|
+
this.addGenerators(g);
|
|
50
50
|
}
|
|
51
51
|
}
|
|
52
52
|
|
|
@@ -63,7 +63,7 @@ export class InstrumentZone extends BasicInstrumentZone
|
|
|
63
63
|
{
|
|
64
64
|
throw new Error("Missing modulator in instrument zone! The file may corrupted.");
|
|
65
65
|
}
|
|
66
|
-
this.
|
|
66
|
+
this.addModulators(m);
|
|
67
67
|
}
|
|
68
68
|
}
|
|
69
69
|
|
|
@@ -79,32 +79,6 @@ export class InstrumentZone extends BasicInstrumentZone
|
|
|
79
79
|
this.setSample(samples[sampleID.generatorValue]);
|
|
80
80
|
}
|
|
81
81
|
}
|
|
82
|
-
|
|
83
|
-
/**
|
|
84
|
-
* Reads the keyRange of the zone
|
|
85
|
-
*/
|
|
86
|
-
getKeyRange()
|
|
87
|
-
{
|
|
88
|
-
let range = this.generators.find(g => g.generatorType === generatorTypes.keyRange);
|
|
89
|
-
if (range)
|
|
90
|
-
{
|
|
91
|
-
this.keyRange.min = range.generatorValue & 0x7F;
|
|
92
|
-
this.keyRange.max = (range.generatorValue >> 8) & 0x7F;
|
|
93
|
-
}
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
/**
|
|
97
|
-
* reads the velolicty range of the zone
|
|
98
|
-
*/
|
|
99
|
-
getVelRange()
|
|
100
|
-
{
|
|
101
|
-
let range = this.generators.find(g => g.generatorType === generatorTypes.velRange);
|
|
102
|
-
if (range)
|
|
103
|
-
{
|
|
104
|
-
this.velRange.min = range.generatorValue & 0x7F;
|
|
105
|
-
this.velRange.max = (range.generatorValue >> 8) & 0x7F;
|
|
106
|
-
}
|
|
107
|
-
}
|
|
108
82
|
}
|
|
109
83
|
|
|
110
84
|
/**
|
|
@@ -132,8 +106,6 @@ export function readInstrumentZones(zonesChunk, instrumentGenerators, instrument
|
|
|
132
106
|
zones[zones.length - 1].getGenerators(instrumentGenerators);
|
|
133
107
|
zones[zones.length - 1].getModulators(instrumentModulators);
|
|
134
108
|
zones[zones.length - 1].getSample(instrumentSamples);
|
|
135
|
-
zones[zones.length - 1].getKeyRange();
|
|
136
|
-
zones[zones.length - 1].getVelRange();
|
|
137
109
|
}
|
|
138
110
|
zones.push(zone);
|
|
139
111
|
}
|
|
@@ -179,7 +151,7 @@ export class PresetZone extends BasicPresetZone
|
|
|
179
151
|
{
|
|
180
152
|
throw new Error("Missing generator in preset zone! The file may corrupted.");
|
|
181
153
|
}
|
|
182
|
-
this.
|
|
154
|
+
this.addGenerators(g);
|
|
183
155
|
}
|
|
184
156
|
}
|
|
185
157
|
|
|
@@ -196,7 +168,7 @@ export class PresetZone extends BasicPresetZone
|
|
|
196
168
|
{
|
|
197
169
|
throw new Error("Missing modulator in preset zone! The file may corrupted.");
|
|
198
170
|
}
|
|
199
|
-
this.
|
|
171
|
+
this.addModulators(m);
|
|
200
172
|
}
|
|
201
173
|
}
|
|
202
174
|
|
|
@@ -212,32 +184,6 @@ export class PresetZone extends BasicPresetZone
|
|
|
212
184
|
this.setInstrument(instruments[instrumentID.generatorValue]);
|
|
213
185
|
}
|
|
214
186
|
}
|
|
215
|
-
|
|
216
|
-
/**
|
|
217
|
-
* Reads the keyRange of the zone
|
|
218
|
-
*/
|
|
219
|
-
getKeyRange()
|
|
220
|
-
{
|
|
221
|
-
let range = this.generators.find(g => g.generatorType === generatorTypes.keyRange);
|
|
222
|
-
if (range)
|
|
223
|
-
{
|
|
224
|
-
this.keyRange.min = range.generatorValue & 0x7F;
|
|
225
|
-
this.keyRange.max = (range.generatorValue >> 8) & 0x7F;
|
|
226
|
-
}
|
|
227
|
-
}
|
|
228
|
-
|
|
229
|
-
/**
|
|
230
|
-
* reads the velolicty range of the zone
|
|
231
|
-
*/
|
|
232
|
-
getVelRange()
|
|
233
|
-
{
|
|
234
|
-
let range = this.generators.find(g => g.generatorType === generatorTypes.velRange);
|
|
235
|
-
if (range)
|
|
236
|
-
{
|
|
237
|
-
this.velRange.min = range.generatorValue & 0x7F;
|
|
238
|
-
this.velRange.max = (range.generatorValue >> 8) & 0x7F;
|
|
239
|
-
}
|
|
240
|
-
}
|
|
241
187
|
}
|
|
242
188
|
|
|
243
189
|
/**
|
|
@@ -265,8 +211,6 @@ export function readPresetZones(zonesChunk, presetGenerators, presetModulators,
|
|
|
265
211
|
zones[zones.length - 1].getGenerators(presetGenerators);
|
|
266
212
|
zones[zones.length - 1].getModulators(presetModulators);
|
|
267
213
|
zones[zones.length - 1].getInstrument(instruments);
|
|
268
|
-
zones[zones.length - 1].getKeyRange();
|
|
269
|
-
zones[zones.length - 1].getVelRange();
|
|
270
214
|
}
|
|
271
215
|
zones.push(zone);
|
|
272
216
|
}
|
|
@@ -3,7 +3,6 @@ import { VolumeEnvelope } from "./volume_envelope.js";
|
|
|
3
3
|
import { ModulationEnvelope } from "./modulation_envelope.js";
|
|
4
4
|
import { Modulator, modulatorSources } from "../../../soundfont/basic_soundfont/modulator.js";
|
|
5
5
|
import { NON_CC_INDEX_OFFSET } from "./controller_tables.js";
|
|
6
|
-
import { SpessaSynthWarn } from "../../../utils/loggin.js";
|
|
7
6
|
import { generatorLimits, generatorTypes } from "../../../soundfont/basic_soundfont/generator_types.js";
|
|
8
7
|
|
|
9
8
|
/**
|
|
@@ -146,22 +145,23 @@ export function computeModulators(voice, sourceUsesCC = -1, sourceIndex = 0)
|
|
|
146
145
|
modulatedGenerators.set(generators);
|
|
147
146
|
modulators.forEach(mod =>
|
|
148
147
|
{
|
|
149
|
-
|
|
150
|
-
if (!limits)
|
|
151
|
-
{
|
|
152
|
-
SpessaSynthWarn(`Invalid modulator: ${mod.modulatorDestination}`);
|
|
153
|
-
return;
|
|
154
|
-
}
|
|
155
|
-
const newValue = modulatedGenerators[mod.modulatorDestination] + computeModulator(
|
|
148
|
+
modulatedGenerators[mod.modulatorDestination] += computeModulator(
|
|
156
149
|
this.midiControllers,
|
|
157
150
|
mod,
|
|
158
151
|
voice
|
|
159
152
|
);
|
|
160
|
-
modulatedGenerators[mod.modulatorDestination] = Math.max(
|
|
161
|
-
limits.min,
|
|
162
|
-
Math.min(newValue, limits.max)
|
|
163
|
-
);
|
|
164
153
|
});
|
|
154
|
+
// apply limits
|
|
155
|
+
for (let gen = 0; gen < modulatedGenerators.length; gen++)
|
|
156
|
+
{
|
|
157
|
+
const limit = generatorLimits[gen];
|
|
158
|
+
if (!limit)
|
|
159
|
+
{
|
|
160
|
+
// skip unused
|
|
161
|
+
continue;
|
|
162
|
+
}
|
|
163
|
+
modulatedGenerators[gen] = Math.min(limit.max, Math.max(limit.min, modulatedGenerators[gen]));
|
|
164
|
+
}
|
|
165
165
|
VolumeEnvelope.recalculate(voice);
|
|
166
166
|
ModulationEnvelope.recalculate(voice);
|
|
167
167
|
return;
|
|
@@ -201,14 +201,15 @@ export function computeModulators(voice, sourceUsesCC = -1, sourceIndex = 0)
|
|
|
201
201
|
{
|
|
202
202
|
if (m.modulatorDestination === destination)
|
|
203
203
|
{
|
|
204
|
-
|
|
205
|
-
const newValue = modulatedGenerators[mod.modulatorDestination] + m.currentValue;
|
|
206
|
-
modulatedGenerators[mod.modulatorDestination] = Math.max(
|
|
207
|
-
limits.min,
|
|
208
|
-
Math.min(newValue, limits.max)
|
|
209
|
-
);
|
|
204
|
+
modulatedGenerators[destination] += m.currentValue;
|
|
210
205
|
}
|
|
211
206
|
});
|
|
207
|
+
// apply limits
|
|
208
|
+
const limits = generatorLimits[destination];
|
|
209
|
+
modulatedGenerators[destination] = Math.max(
|
|
210
|
+
limits.min,
|
|
211
|
+
Math.min(modulatedGenerators[destination], limits.max)
|
|
212
|
+
);
|
|
212
213
|
computedDestinations.add(destination);
|
|
213
214
|
}
|
|
214
215
|
}
|
|
@@ -238,8 +238,4 @@ export function resetParameters()
|
|
|
238
238
|
this.midiControllers[midiControllers.RPNMsb] = 127 << 7;
|
|
239
239
|
this.resetGeneratorOverrides();
|
|
240
240
|
this.resetGeneratorOffsets();
|
|
241
|
-
SpessaSynthInfo(
|
|
242
|
-
"%cResetting Registered and Non-Registered Parameters!",
|
|
243
|
-
consoleColors.info
|
|
244
|
-
);
|
|
245
241
|
}
|