spessasynth_lib 3.24.28 → 3.24.35
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/README.md +1 -1
- package/external_midi/web_midi_link.js +1 -1
- package/index.js +8 -52
- package/midi_parser/basic_midi.js +15 -30
- package/midi_parser/midi_builder.js +6 -6
- package/midi_parser/midi_data.js +2 -2
- package/midi_parser/midi_editor.js +15 -15
- package/midi_parser/midi_loader.js +3 -3
- package/midi_parser/midi_message.js +2 -2
- package/midi_parser/midi_sequence.js +32 -4
- package/midi_parser/midi_writer.js +8 -8
- package/midi_parser/rmidi_writer.js +20 -21
- package/midi_parser/used_keys_loaded.js +8 -6
- package/package.json +1 -1
- package/sequencer/sequencer.js +6 -6
- package/sequencer/worklet_sequencer/events.js +2 -1
- package/sequencer/worklet_sequencer/play.js +2 -3
- package/sequencer/worklet_sequencer/process_event.js +1 -1
- package/sequencer/worklet_sequencer/process_tick.js +1 -1
- package/sequencer/worklet_sequencer/sequencer_message.js +1 -1
- package/sequencer/worklet_sequencer/song_control.js +6 -7
- package/sequencer/worklet_sequencer/worklet_sequencer.js +2 -1
- package/soundfont/basic_soundfont/basic_soundfont.js +197 -13
- package/soundfont/basic_soundfont/generator.js +18 -16
- package/soundfont/basic_soundfont/modulator.js +3 -0
- package/soundfont/basic_soundfont/riff_chunk.js +2 -2
- package/soundfont/basic_soundfont/write_dls/art2.js +1 -1
- package/soundfont/basic_soundfont/write_dls/combine_zones.js +12 -10
- package/soundfont/basic_soundfont/write_dls/ins.js +2 -2
- package/soundfont/basic_soundfont/write_dls/lins.js +1 -1
- package/soundfont/basic_soundfont/write_dls/modulator_converter.js +9 -9
- package/soundfont/basic_soundfont/write_dls/rgn2.js +5 -5
- package/soundfont/basic_soundfont/write_dls/wave.js +2 -1
- package/soundfont/basic_soundfont/write_dls/write_dls.js +1 -1
- package/soundfont/basic_soundfont/write_dls/wsmp.js +2 -2
- package/soundfont/basic_soundfont/write_dls/wvpl.js +1 -1
- package/soundfont/basic_soundfont/write_sf2/ibag.js +2 -2
- package/soundfont/basic_soundfont/write_sf2/igen.js +20 -17
- package/soundfont/basic_soundfont/write_sf2/imod.js +3 -3
- package/soundfont/basic_soundfont/write_sf2/inst.js +2 -2
- package/soundfont/basic_soundfont/write_sf2/pbag.js +2 -2
- package/soundfont/basic_soundfont/write_sf2/pgen.js +21 -18
- package/soundfont/basic_soundfont/write_sf2/phdr.js +2 -2
- package/soundfont/basic_soundfont/write_sf2/pmod.js +2 -2
- package/soundfont/basic_soundfont/write_sf2/sdta.js +1 -1
- package/soundfont/basic_soundfont/write_sf2/shdr.js +1 -1
- package/soundfont/basic_soundfont/write_sf2/write.js +7 -6
- package/soundfont/dls/articulator_converter.js +1 -0
- package/soundfont/dls/dls_destinations.js +3 -3
- package/soundfont/dls/dls_preset.js +1 -1
- package/soundfont/dls/dls_soundfont.js +7 -7
- package/soundfont/dls/read_instrument.js +1 -1
- package/soundfont/dls/read_region.js +1 -0
- package/soundfont/dls/read_samples.js +4 -5
- package/soundfont/load_soundfont.js +1 -1
- package/soundfont/read_sf2/presets.js +1 -1
- package/soundfont/read_sf2/soundfont.js +5 -5
- package/synthetizer/audio_effects/effects_config.js +3 -3
- package/synthetizer/audio_effects/fancy_chorus.js +7 -7
- package/synthetizer/audio_effects/reverb.js +5 -5
- package/synthetizer/audio_effects/reverb_as_binary.js +15 -0
- package/synthetizer/audio_effects/reverb_buffer.min.js +1 -0
- package/synthetizer/synth_constants.js +14 -0
- package/synthetizer/synthetizer.js +1 -17
- package/synthetizer/worklet_processor.min.js +14 -12
- package/synthetizer/worklet_system/main_processor.js +2 -2
- package/synthetizer/worklet_system/worklet_methods/controller_control/controller_change.js +1 -1
- package/synthetizer/worklet_system/worklet_methods/controller_control/reset_controllers.js +1 -1
- package/synthetizer/worklet_system/worklet_methods/create_worklet_channel.js +2 -1
- package/synthetizer/worklet_system/worklet_methods/worklet_soundfont_manager/worklet_soundfont_manager.js +2 -2
- package/synthetizer/worklet_system/worklet_processor.js +1 -1
- package/soundfont/basic_soundfont/write_sf2/soundfont_trimmer.js +0 -185
- package/synthetizer/audio_effects/impulse_response_2.flac +0 -0
- package/synthetizer/worklet_system/minify_processor.sh +0 -4
|
@@ -22,7 +22,7 @@ const notGlobalizedTypes = new Set([
|
|
|
22
22
|
generatorTypes.coarseTune, // written into wsmp, there's no global wsmp
|
|
23
23
|
generatorTypes.keyNumToVolEnvHold, // KEY TO SOMETHING:
|
|
24
24
|
generatorTypes.keyNumToVolEnvDecay,// cannot be globalized as they modify their respective generators
|
|
25
|
-
generatorTypes.keyNumToModEnvHold, // (for example
|
|
25
|
+
generatorTypes.keyNumToModEnvHold, // (for example, keyNumToVolEnvDecay modifies VolEnvDecay)
|
|
26
26
|
generatorTypes.keyNumToModEnvDecay
|
|
27
27
|
]);
|
|
28
28
|
|
|
@@ -78,7 +78,7 @@ export function combineZones(preset, globalize = true)
|
|
|
78
78
|
let globalPresetKeyRange = { min: 0, max: 127 };
|
|
79
79
|
let globalPresetVelRange = { min: 0, max: 127 };
|
|
80
80
|
|
|
81
|
-
// find the global zone and apply ranges, generators and modulators
|
|
81
|
+
// find the global zone and apply ranges, generators, and modulators
|
|
82
82
|
const globalPresetZone = preset.presetZones.find(z => z.isGlobal);
|
|
83
83
|
if (globalPresetZone)
|
|
84
84
|
{
|
|
@@ -151,7 +151,7 @@ export function combineZones(preset, globalize = true)
|
|
|
151
151
|
instZoneKeyRange = subtractRanges(instZoneKeyRange, presetZoneKeyRange);
|
|
152
152
|
instZoneVelRange = subtractRanges(instZoneVelRange, presetZoneVelRange);
|
|
153
153
|
|
|
154
|
-
// if either of the zones is out of range (i.e. min larger than max)
|
|
154
|
+
// if either of the zones is out of range (i.e.m min larger than the max),
|
|
155
155
|
// then we discard that zone
|
|
156
156
|
if (instZoneKeyRange.max < instZoneKeyRange.min || instZoneVelRange.max < instZoneVelRange.min)
|
|
157
157
|
{
|
|
@@ -175,7 +175,9 @@ export function combineZones(preset, globalize = true)
|
|
|
175
175
|
m => Modulator.isIdentical(mod, m));
|
|
176
176
|
if (identicalInstMod !== -1)
|
|
177
177
|
{
|
|
178
|
-
// sum the amounts
|
|
178
|
+
// sum the amounts
|
|
179
|
+
// (this makes a new modulator
|
|
180
|
+
// because otherwise it would overwrite the one in the soundfont!
|
|
179
181
|
finalModList[identicalInstMod] = finalModList[identicalInstMod].sumTransform(
|
|
180
182
|
mod);
|
|
181
183
|
}
|
|
@@ -308,10 +310,10 @@ export function combineZones(preset, globalize = true)
|
|
|
308
310
|
break;
|
|
309
311
|
}
|
|
310
312
|
}
|
|
311
|
-
// if at least one
|
|
313
|
+
// if at least one occurrence, find the most used one and add it to global
|
|
312
314
|
if (Object.keys(occurencesForValues).length > 0)
|
|
313
315
|
{
|
|
314
|
-
// [value,
|
|
316
|
+
// [value, occurrences]
|
|
315
317
|
const valueToGlobalize = Object.entries(occurencesForValues).reduce((max, curr) =>
|
|
316
318
|
{
|
|
317
319
|
if (max[1] < curr[1])
|
|
@@ -336,13 +338,13 @@ export function combineZones(preset, globalize = true)
|
|
|
336
338
|
{
|
|
337
339
|
if (z.generators[gen].generatorValue === targetValue)
|
|
338
340
|
{
|
|
339
|
-
//
|
|
341
|
+
// That exact value exists. Since it's global now, remove it
|
|
340
342
|
z.generators.splice(gen, 1);
|
|
341
343
|
}
|
|
342
344
|
}
|
|
343
345
|
else
|
|
344
346
|
{
|
|
345
|
-
//
|
|
347
|
+
// That type does not exist at all here.
|
|
346
348
|
// Since we're globalizing, we need to add the default here.
|
|
347
349
|
if (targetValue !== defaultForChecked)
|
|
348
350
|
{
|
|
@@ -378,13 +380,13 @@ export function combineZones(preset, globalize = true)
|
|
|
378
380
|
if (existsForAllZones === true)
|
|
379
381
|
{
|
|
380
382
|
globalZone.modulators.push(Modulator.copy(checkedModulator));
|
|
381
|
-
// delete from local zones.
|
|
383
|
+
// delete it from local zones.
|
|
382
384
|
for (const zone of finalZones)
|
|
383
385
|
{
|
|
384
386
|
const modulator = zone.modulators.find(m => Modulator.isIdentical(m, checkedModulator));
|
|
385
387
|
// Check if the amount is correct.
|
|
386
388
|
// If so, delete it since it's global.
|
|
387
|
-
//
|
|
389
|
+
// If not, then it will simply override global as it's identical.
|
|
388
390
|
if (modulator.transformAmount === checkedModulator.transformAmount)
|
|
389
391
|
{
|
|
390
392
|
zone.modulators.splice(zone.modulators.indexOf(modulator), 1);
|
|
@@ -9,7 +9,7 @@ import { SpessaSynthGroupCollapsed, SpessaSynthGroupEnd } from "../../../utils/l
|
|
|
9
9
|
import { consoleColors } from "../../../utils/other.js";
|
|
10
10
|
|
|
11
11
|
/**
|
|
12
|
-
* @this {
|
|
12
|
+
* @this {BasicSoundBank}
|
|
13
13
|
* @param preset {BasicPreset}
|
|
14
14
|
* @returns {IndexedByteArray}
|
|
15
15
|
*/
|
|
@@ -65,7 +65,7 @@ export function writeIns(preset)
|
|
|
65
65
|
);
|
|
66
66
|
}
|
|
67
67
|
|
|
68
|
-
// write region list
|
|
68
|
+
// write the region list
|
|
69
69
|
const lrgnData = combineArrays(combined.reduce((arrs, z) =>
|
|
70
70
|
{
|
|
71
71
|
if (!z.isGlobal)
|
|
@@ -75,7 +75,7 @@ function getDLSDestinationFromSf2(dest, amount)
|
|
|
75
75
|
return undefined;
|
|
76
76
|
|
|
77
77
|
case generatorTypes.initialAttenuation:
|
|
78
|
-
// amount does not get EMU corrected here, as this only applies to modulator attenuation
|
|
78
|
+
// the amount does not get EMU corrected here, as this only applies to modulator attenuation
|
|
79
79
|
// the generator (affected) attenuation is handled in wsmp.
|
|
80
80
|
return { dest: DLSDestinations.gain, amount: -amount };
|
|
81
81
|
case generatorTypes.fineTune:
|
|
@@ -193,10 +193,10 @@ function checkSF2SpecialCombos(dest, amt)
|
|
|
193
193
|
isBipolar: true
|
|
194
194
|
};
|
|
195
195
|
|
|
196
|
-
//
|
|
197
|
-
// keyNum to relative pitch at
|
|
196
|
+
// Scale tuning is implemented in DLS via an articulator:
|
|
197
|
+
// keyNum to relative pitch at 12,800 cents.
|
|
198
198
|
// Change that to scale tuning * 128.
|
|
199
|
-
//
|
|
199
|
+
// Therefore, a regular scale is still 12,800, half is 6400, etc.
|
|
200
200
|
case generatorTypes.scaleTuning:
|
|
201
201
|
return {
|
|
202
202
|
source: DLSSources.keyNum,
|
|
@@ -265,7 +265,7 @@ export function getDLSArticulatorFromSf2Modulator(mod)
|
|
|
265
265
|
SpessaSynthWarn(`Invalid source: ${mod.sourceIndex}, CC: ${mod.sourceUsesCC}`);
|
|
266
266
|
return undefined;
|
|
267
267
|
}
|
|
268
|
-
//
|
|
268
|
+
// Attenuation is the opposite of gain. Invert.
|
|
269
269
|
if (mod.modulatorDestination === generatorTypes.initialAttenuation)
|
|
270
270
|
{
|
|
271
271
|
sourceDirection = sourceDirection === 1 ? 0 : 1;
|
|
@@ -291,13 +291,13 @@ export function getDLSArticulatorFromSf2Modulator(mod)
|
|
|
291
291
|
if (specialCombo !== undefined)
|
|
292
292
|
{
|
|
293
293
|
amt = specialCombo.amt;
|
|
294
|
-
// move source to control
|
|
294
|
+
// move the source to control
|
|
295
295
|
control = source;
|
|
296
296
|
controlTransformType = sourceTransformType;
|
|
297
297
|
controlBipolar = sourceBipolar;
|
|
298
298
|
controlDirection = sourceDirection;
|
|
299
299
|
|
|
300
|
-
// set source as static as it's either: env, lfo or
|
|
300
|
+
// set source as static as it's either: env, lfo or key num
|
|
301
301
|
sourceTransformType = modulatorCurveTypes.linear;
|
|
302
302
|
sourceBipolar = specialCombo.isBipolar ? 1 : 0;
|
|
303
303
|
sourceDirection = 0;
|
|
@@ -310,13 +310,13 @@ export function getDLSArticulatorFromSf2Modulator(mod)
|
|
|
310
310
|
return undefined;
|
|
311
311
|
}
|
|
312
312
|
|
|
313
|
-
// source curve type maps to desfont curve type in section 2.10, table 9
|
|
313
|
+
// source curve type maps to a desfont curve type in section 2.10, table 9
|
|
314
314
|
let transform = 0;
|
|
315
315
|
transform |= controlTransformType << 4;
|
|
316
316
|
transform |= controlBipolar << 8;
|
|
317
317
|
transform |= controlDirection << 9;
|
|
318
318
|
|
|
319
|
-
// use
|
|
319
|
+
// use the source curve in output transform
|
|
320
320
|
transform |= sourceTransformType;
|
|
321
321
|
transform |= sourceBipolar << 14;
|
|
322
322
|
transform |= sourceDirection << 15;
|
|
@@ -8,7 +8,7 @@ import { writeArticulator } from "./art2.js";
|
|
|
8
8
|
/**
|
|
9
9
|
* @param zone {BasicInstrumentZone}
|
|
10
10
|
* @param globalZone {BasicInstrumentZone}
|
|
11
|
-
* @this {
|
|
11
|
+
* @this {BasicSoundBank}
|
|
12
12
|
* @returns {IndexedByteArray}
|
|
13
13
|
*/
|
|
14
14
|
export function writeDLSRegion(zone, globalZone)
|
|
@@ -35,8 +35,8 @@ export function writeDLSRegion(zone, globalZone)
|
|
|
35
35
|
|
|
36
36
|
let rootKey = zone.getGeneratorValue(generatorTypes.overridingRootKey, zone.sample.samplePitch);
|
|
37
37
|
|
|
38
|
-
// a lot of soundfonts like to set
|
|
39
|
-
// since we implement
|
|
38
|
+
// a lot of soundfonts like to set scale tuning to 0 in drums and keep the key at 60
|
|
39
|
+
// since we implement scale tuning via a dls articulator and fluid doesn't support these,
|
|
40
40
|
// change the root key here
|
|
41
41
|
const scaleTuning = zone.getGeneratorValue(
|
|
42
42
|
generatorTypes.scaleTuning,
|
|
@@ -47,7 +47,7 @@ export function writeDLSRegion(zone, globalZone)
|
|
|
47
47
|
rootKey = zone.keyRange.min;
|
|
48
48
|
}
|
|
49
49
|
|
|
50
|
-
//
|
|
50
|
+
// wave sample (Wsmp)
|
|
51
51
|
const wsmp = writeWavesample(
|
|
52
52
|
zone.sample,
|
|
53
53
|
rootKey,
|
|
@@ -67,7 +67,7 @@ export function writeDLSRegion(zone, globalZone)
|
|
|
67
67
|
zone.getGeneratorValue(generatorTypes.sampleModes, 0)
|
|
68
68
|
);
|
|
69
69
|
|
|
70
|
-
//
|
|
70
|
+
// wave link (wlnk)
|
|
71
71
|
const wlnkData = new IndexedByteArray(12);
|
|
72
72
|
writeWord(wlnkData, 0); // fusOptions
|
|
73
73
|
writeWord(wlnkData, 0); // usPhaseGroup
|
|
@@ -34,10 +34,10 @@ export function writeWavesample(
|
|
|
34
34
|
// gain correction, use InitialAttenuation, apply attenuation correction
|
|
35
35
|
const attenuationCb = attenuationCentibels * 0.4;
|
|
36
36
|
|
|
37
|
-
// gain correction:
|
|
37
|
+
// gain correction: Each unit of gain represents 1/655360 dB
|
|
38
38
|
const lGain = Math.floor(attenuationCb * -65536);
|
|
39
39
|
writeDword(wsmpData, lGain);
|
|
40
|
-
// fulOptions: has to be 2 according to all DLS files I have
|
|
40
|
+
// fulOptions: has to be 2, according to all DLS files I have
|
|
41
41
|
writeDword(wsmpData, 2);
|
|
42
42
|
|
|
43
43
|
const loopSize = loopEnd - loopStart;
|
|
@@ -3,7 +3,7 @@ import { writeRIFFOddSize } from "../riff_chunk.js";
|
|
|
3
3
|
import { combineArrays } from "../../../utils/indexed_array.js";
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
|
-
* @this {
|
|
6
|
+
* @this {BasicSoundBank}
|
|
7
7
|
* @returns {{data: IndexedByteArray, indexes: number[] }}
|
|
8
8
|
*/
|
|
9
9
|
export function writeWavePool()
|
|
@@ -3,12 +3,12 @@ import { writeWord } from "../../../utils/byte_functions/little_endian.js";
|
|
|
3
3
|
import { RiffChunk, writeRIFFChunk } from "../riff_chunk.js";
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
|
-
* @this {
|
|
6
|
+
* @this {BasicSoundBank}
|
|
7
7
|
* @returns {IndexedByteArray}
|
|
8
8
|
*/
|
|
9
9
|
export function getIBAG()
|
|
10
10
|
{
|
|
11
|
-
// write all
|
|
11
|
+
// write all ibag with their start indexes as they were changed in getIGEN() and getIMOD()
|
|
12
12
|
const ibagsize = this.instruments.reduce((sum, i) => i.instrumentZones.length * 4 + sum, 4);
|
|
13
13
|
const ibagdata = new IndexedByteArray(ibagsize);
|
|
14
14
|
let zoneID = 0;
|
|
@@ -2,10 +2,10 @@ import { writeDword, writeWord } from "../../../utils/byte_functions/little_endi
|
|
|
2
2
|
import { IndexedByteArray } from "../../../utils/indexed_array.js";
|
|
3
3
|
import { RiffChunk, writeRIFFChunk } from "../riff_chunk.js";
|
|
4
4
|
|
|
5
|
-
import { generatorTypes } from "../generator.js";
|
|
5
|
+
import { Generator, generatorTypes } from "../generator.js";
|
|
6
6
|
|
|
7
7
|
/**
|
|
8
|
-
* @this {
|
|
8
|
+
* @this {BasicSoundBank}
|
|
9
9
|
* @returns {IndexedByteArray}
|
|
10
10
|
*/
|
|
11
11
|
export function getIGEN()
|
|
@@ -16,35 +16,38 @@ export function getIGEN()
|
|
|
16
16
|
{
|
|
17
17
|
igensize += inst.instrumentZones.reduce((sum, z) =>
|
|
18
18
|
{
|
|
19
|
-
// clear sample and range generators before
|
|
19
|
+
// clear sample and range generators before determining the size
|
|
20
20
|
z.generators = z.generators.filter(g =>
|
|
21
21
|
g.generatorType !== generatorTypes.sampleID &&
|
|
22
22
|
g.generatorType !== generatorTypes.keyRange &&
|
|
23
23
|
g.generatorType !== generatorTypes.velRange
|
|
24
24
|
);
|
|
25
|
-
// add sample and ranges if
|
|
26
|
-
// unshift vel then key (
|
|
25
|
+
// add sample and ranges if necessary
|
|
26
|
+
// unshift vel then key (to make key first) and the instrument is last
|
|
27
27
|
if (z.velRange.max !== 127 || z.velRange.min !== 0)
|
|
28
28
|
{
|
|
29
|
-
z.generators.unshift(
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
29
|
+
z.generators.unshift(new Generator(
|
|
30
|
+
generatorTypes.velRange,
|
|
31
|
+
z.velRange.max << 8 | Math.max(z.velRange.min, 0),
|
|
32
|
+
false
|
|
33
|
+
));
|
|
33
34
|
}
|
|
34
35
|
if (z.keyRange.max !== 127 || z.keyRange.min !== 0)
|
|
35
36
|
{
|
|
36
|
-
z.generators.unshift(
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
37
|
+
z.generators.unshift(new Generator(
|
|
38
|
+
generatorTypes.keyRange,
|
|
39
|
+
z.keyRange.max << 8 | Math.max(z.keyRange.min, 0),
|
|
40
|
+
false
|
|
41
|
+
));
|
|
40
42
|
}
|
|
41
43
|
if (!z.isGlobal)
|
|
42
44
|
{
|
|
43
45
|
// write sample
|
|
44
|
-
z.generators.push(
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
46
|
+
z.generators.push(new Generator(
|
|
47
|
+
generatorTypes.sampleID,
|
|
48
|
+
this.samples.indexOf(z.sample),
|
|
49
|
+
false
|
|
50
|
+
));
|
|
48
51
|
}
|
|
49
52
|
return z.generators.length * 4 + sum;
|
|
50
53
|
}, 0);
|
|
@@ -3,13 +3,13 @@ import { writeLittleEndian, writeWord } from "../../../utils/byte_functions/litt
|
|
|
3
3
|
import { RiffChunk, writeRIFFChunk } from "../riff_chunk.js";
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
|
-
* @this {
|
|
6
|
+
* @this {BasicSoundBank}
|
|
7
7
|
* @returns {IndexedByteArray}
|
|
8
8
|
*/
|
|
9
9
|
export function getIMOD()
|
|
10
10
|
{
|
|
11
|
-
// very similar to igen
|
|
12
|
-
// go through all instruments ->
|
|
11
|
+
// very similar to igen,
|
|
12
|
+
// go through all instruments -> zones and write modulators sequentially
|
|
13
13
|
let imodsize = 10;
|
|
14
14
|
for (const inst of this.instruments)
|
|
15
15
|
{
|
|
@@ -4,14 +4,14 @@ import { writeWord } from "../../../utils/byte_functions/little_endian.js";
|
|
|
4
4
|
import { RiffChunk, writeRIFFChunk } from "../riff_chunk.js";
|
|
5
5
|
|
|
6
6
|
/**
|
|
7
|
-
* @this {
|
|
7
|
+
* @this {BasicSoundBank}
|
|
8
8
|
* @returns {IndexedByteArray}
|
|
9
9
|
*/
|
|
10
10
|
export function getINST()
|
|
11
11
|
{
|
|
12
12
|
const instsize = this.instruments.length * 22 + 22;
|
|
13
13
|
const instdata = new IndexedByteArray(instsize);
|
|
14
|
-
// the instrument start index is adjusted in ibag,
|
|
14
|
+
// the instrument start index is adjusted in ibag, write it here
|
|
15
15
|
let instrumentStart = 0;
|
|
16
16
|
let instrumentID = 0;
|
|
17
17
|
for (const inst of this.instruments)
|
|
@@ -3,12 +3,12 @@ import { writeWord } from "../../../utils/byte_functions/little_endian.js";
|
|
|
3
3
|
import { RiffChunk, writeRIFFChunk } from "../riff_chunk.js";
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
|
-
* @this {
|
|
6
|
+
* @this {BasicSoundBank}
|
|
7
7
|
* @returns {IndexedByteArray}
|
|
8
8
|
*/
|
|
9
9
|
export function getPBAG()
|
|
10
10
|
{
|
|
11
|
-
// write all
|
|
11
|
+
// write all pbag with their start indexes as they were changed in getPGEN() and getPMOD()
|
|
12
12
|
const pbagsize = this.presets.reduce((sum, i) => i.presetZones.length * 4 + sum, 4);
|
|
13
13
|
const pbagdata = new IndexedByteArray(pbagsize);
|
|
14
14
|
let zoneID = 0;
|
|
@@ -2,22 +2,22 @@ import { 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 { generatorTypes } from "../generator.js";
|
|
5
|
+
import { Generator, generatorTypes } from "../generator.js";
|
|
6
6
|
|
|
7
7
|
/**
|
|
8
|
-
* @this {
|
|
8
|
+
* @this {BasicSoundBank}
|
|
9
9
|
* @returns {IndexedByteArray}
|
|
10
10
|
*/
|
|
11
11
|
export function getPGEN()
|
|
12
12
|
{
|
|
13
|
-
// almost identical to igen, except correct instrument instead of sample gen
|
|
14
|
-
//
|
|
13
|
+
// almost identical to igen, except the correct instrument instead of sample gen
|
|
14
|
+
// goes through all preset zones and writes generators sequentially (add 4 for terminal)
|
|
15
15
|
let pgensize = 4;
|
|
16
16
|
for (const preset of this.presets)
|
|
17
17
|
{
|
|
18
18
|
pgensize += preset.presetZones.reduce((size, z) =>
|
|
19
19
|
{
|
|
20
|
-
// clear instrument and range generators before
|
|
20
|
+
// clear instrument and range generators before determining the size
|
|
21
21
|
z.generators = z.generators.filter(g =>
|
|
22
22
|
g.generatorType !== generatorTypes.instrument &&
|
|
23
23
|
g.generatorType !== generatorTypes.keyRange &&
|
|
@@ -26,25 +26,28 @@ export function getPGEN()
|
|
|
26
26
|
// unshift vel then key and instrument is last
|
|
27
27
|
if (z.velRange.max !== 127 || z.velRange.min !== 0)
|
|
28
28
|
{
|
|
29
|
-
z.generators.unshift(
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
29
|
+
z.generators.unshift(new Generator(
|
|
30
|
+
generatorTypes.velRange,
|
|
31
|
+
z.velRange.max << 8 | Math.max(z.velRange.min, 0),
|
|
32
|
+
false
|
|
33
|
+
));
|
|
33
34
|
}
|
|
34
35
|
if (z.keyRange.max !== 127 || z.keyRange.min !== 0)
|
|
35
36
|
{
|
|
36
|
-
z.generators.unshift(
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
37
|
+
z.generators.unshift(new Generator(
|
|
38
|
+
generatorTypes.keyRange,
|
|
39
|
+
z.keyRange.max << 8 | Math.max(z.keyRange.min, 0),
|
|
40
|
+
false
|
|
41
|
+
));
|
|
40
42
|
}
|
|
41
43
|
if (!z.isGlobal)
|
|
42
44
|
{
|
|
43
|
-
// write instrument
|
|
44
|
-
z.generators.push(
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
45
|
+
// write the instrument
|
|
46
|
+
z.generators.push(new Generator(
|
|
47
|
+
generatorTypes.instrument,
|
|
48
|
+
this.instruments.indexOf(z.instrument),
|
|
49
|
+
false
|
|
50
|
+
));
|
|
48
51
|
}
|
|
49
52
|
return z.generators.length * 4 + size;
|
|
50
53
|
}, 0);
|
|
@@ -4,7 +4,7 @@ import { writeDword, writeWord } from "../../../utils/byte_functions/little_endi
|
|
|
4
4
|
import { RiffChunk, writeRIFFChunk } from "../riff_chunk.js";
|
|
5
5
|
|
|
6
6
|
/**
|
|
7
|
-
* @this {
|
|
7
|
+
* @this {BasicSoundBank}
|
|
8
8
|
* @returns {IndexedByteArray}
|
|
9
9
|
*/
|
|
10
10
|
export function getPHDR()
|
|
@@ -19,7 +19,7 @@ export function getPHDR()
|
|
|
19
19
|
writeWord(phdrdata, preset.program);
|
|
20
20
|
writeWord(phdrdata, preset.bank);
|
|
21
21
|
writeWord(phdrdata, presetStart);
|
|
22
|
-
// 3 unused
|
|
22
|
+
// 3 unused dword, spec says to keep em so we do
|
|
23
23
|
writeDword(phdrdata, preset.library);
|
|
24
24
|
writeDword(phdrdata, preset.genre);
|
|
25
25
|
writeDword(phdrdata, preset.morphology);
|
|
@@ -3,12 +3,12 @@ import { writeLittleEndian, writeWord } from "../../../utils/byte_functions/litt
|
|
|
3
3
|
import { RiffChunk, writeRIFFChunk } from "../riff_chunk.js";
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
|
-
* @this {
|
|
6
|
+
* @this {BasicSoundBank}
|
|
7
7
|
* @returns {IndexedByteArray}
|
|
8
8
|
*/
|
|
9
9
|
export function getPMOD()
|
|
10
10
|
{
|
|
11
|
-
// very similar to imod
|
|
11
|
+
// very similar to imod,
|
|
12
12
|
// go through all presets -> zones and write modulators sequentially
|
|
13
13
|
let pmodsize = 10;
|
|
14
14
|
for (const preset of this.presets)
|
|
@@ -4,7 +4,7 @@ import { SpessaSynthInfo } from "../../../utils/loggin.js";
|
|
|
4
4
|
import { consoleColors } from "../../../utils/other.js";
|
|
5
5
|
|
|
6
6
|
/**
|
|
7
|
-
* @this {
|
|
7
|
+
* @this {BasicSoundBank}
|
|
8
8
|
* @param smplStartOffsets {number[]}
|
|
9
9
|
* @param smplEndOffsets {number[]}
|
|
10
10
|
* @param compress {boolean}
|
|
@@ -4,7 +4,7 @@ import { writeDword, writeWord } from "../../../utils/byte_functions/little_endi
|
|
|
4
4
|
import { RiffChunk, writeRIFFChunk } from "../riff_chunk.js";
|
|
5
5
|
|
|
6
6
|
/**
|
|
7
|
-
* @this {
|
|
7
|
+
* @this {BasicSoundBank}
|
|
8
8
|
* @param smplStartOffsets {number[]}
|
|
9
9
|
* @param smplEndOffsets {number[]}
|
|
10
10
|
* @returns {IndexedByteArray}
|
|
@@ -16,9 +16,10 @@ import { writeWord } from "../../../utils/byte_functions/little_endian.js";
|
|
|
16
16
|
import { SpessaSynthGroupCollapsed, SpessaSynthGroupEnd, SpessaSynthInfo } from "../../../utils/loggin.js";
|
|
17
17
|
/**
|
|
18
18
|
* @typedef {Object} SoundFont2WriteOptions
|
|
19
|
-
* @property {boolean} compress - if the soundfont should be compressed with the
|
|
19
|
+
* @property {boolean} compress - if the soundfont should be compressed with the Ogg Vorbis codec
|
|
20
20
|
* @property {number} compressionQuality - the vorbis compression quality, from -0.1 to 1
|
|
21
|
-
* @property {EncodeVorbisFunction|undefined} compressionFunction - the encode vorbis function.
|
|
21
|
+
* @property {EncodeVorbisFunction|undefined} compressionFunction - the encode vorbis function.
|
|
22
|
+
* Can be undefined if not compressed.
|
|
22
23
|
*/
|
|
23
24
|
|
|
24
25
|
/**
|
|
@@ -32,7 +33,7 @@ const DEFAULT_WRITE_OPTIONS = {
|
|
|
32
33
|
|
|
33
34
|
/**
|
|
34
35
|
* Write the soundfont as an .sf2 file. This method is DESTRUCTIVE
|
|
35
|
-
* @this {
|
|
36
|
+
* @this {BasicSoundBank}
|
|
36
37
|
* @param {SoundFont2WriteOptions} options
|
|
37
38
|
* @returns {Uint8Array}
|
|
38
39
|
*/
|
|
@@ -115,7 +116,7 @@ export function write(options = DEFAULT_WRITE_OPTIONS)
|
|
|
115
116
|
"%cWriting SDTA...",
|
|
116
117
|
consoleColors.info
|
|
117
118
|
);
|
|
118
|
-
// write
|
|
119
|
+
// write sdta
|
|
119
120
|
const smplStartOffsets = [];
|
|
120
121
|
const smplEndOffsets = [];
|
|
121
122
|
const sdtaChunk = getSDTA.call(
|
|
@@ -131,7 +132,7 @@ export function write(options = DEFAULT_WRITE_OPTIONS)
|
|
|
131
132
|
"%cWriting PDTA...",
|
|
132
133
|
consoleColors.info
|
|
133
134
|
);
|
|
134
|
-
// write
|
|
135
|
+
// write pdta
|
|
135
136
|
// go in reverse so the indexes are correct
|
|
136
137
|
// instruments
|
|
137
138
|
SpessaSynthInfo(
|
|
@@ -178,7 +179,7 @@ export function write(options = DEFAULT_WRITE_OPTIONS)
|
|
|
178
179
|
const phdrChunk = getPHDR.call(this);
|
|
179
180
|
// combine in the sfspec order
|
|
180
181
|
const pdtadata = combineArrays([
|
|
181
|
-
new IndexedByteArray([112, 100, 116, 97]), // "
|
|
182
|
+
new IndexedByteArray([112, 100, 116, 97]), // "pdta"
|
|
182
183
|
phdrChunk,
|
|
183
184
|
pbagChunk,
|
|
184
185
|
pmodChunk,
|
|
@@ -9,9 +9,9 @@ export const DLSDestinations = {
|
|
|
9
9
|
pitch: 0x3, // pitch in cents
|
|
10
10
|
pan: 0x4, // pan 10ths of a percent
|
|
11
11
|
keyNum: 0x5, // MIDI key number
|
|
12
|
-
// nuh uh, the channel controllers are not supported
|
|
13
|
-
chorusSend: 0x80, // chorus send 10ths of a percent
|
|
14
|
-
reverbSend: 0x81, // reverb send 10ths of a percent
|
|
12
|
+
// nuh uh, the channel controllers are not supported!
|
|
13
|
+
chorusSend: 0x80, // chorus send level 10ths of a percent
|
|
14
|
+
reverbSend: 0x81, // reverb send level 10ths of a percent
|
|
15
15
|
|
|
16
16
|
modLfoFreq: 0x104, // modulation LFO frequency
|
|
17
17
|
modLfoDelay: 0x105, // modulation LFO delay
|