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.
- package/@types/soundfont/basic_soundfont/basic_preset.d.ts +10 -0
- package/@types/soundfont/basic_soundfont/basic_soundfont.d.ts +8 -2
- package/@types/soundfont/read_sf2/modulators.d.ts +13 -2
- package/@types/soundfont/read_sf2/presets.d.ts +4 -2
- package/package.json +2 -2
- package/soundfont/basic_soundfont/basic_preset.js +16 -19
- package/soundfont/basic_soundfont/basic_soundfont.js +17 -2
- package/soundfont/basic_soundfont/write_sf2/imod.js +2 -2
- package/soundfont/basic_soundfont/write_sf2/pmod.js +2 -2
- package/soundfont/basic_soundfont/write_sf2/write.js +10 -0
- package/soundfont/dls/dls_preset.js +3 -1
- package/soundfont/dls/dls_soundfont.js +6 -1
- package/soundfont/dls/dls_zone.js +1 -0
- package/soundfont/read_sf2/modulators.js +46 -18
- package/soundfont/read_sf2/presets.js +6 -4
- package/soundfont/read_sf2/soundfont.js +18 -3
- package/synthetizer/worklet_processor.min.js +8 -8
- package/synthetizer/worklet_system/worklet_methods/system_exclusive.js +15 -6
- package/synthetizer/worklet_system/worklet_methods/worklet_soundfont_manager/worklet_soundfont_manager.js +11 -0
- package/synthetizer/worklet_system/worklet_utilities/stereo_panner.js +2 -2
|
@@ -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
|
-
|
|
54
|
+
sourceEnum: any;
|
|
44
55
|
/**
|
|
45
56
|
* @type {generatorTypes}
|
|
46
57
|
*/
|
|
47
58
|
modulatorDestination: generatorTypes;
|
|
48
|
-
|
|
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.
|
|
4
|
-
"description": "MIDI and SoundFont2
|
|
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
|
-
|
|
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
|
|
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 =>
|
|
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 =>
|
|
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
|
-
|
|
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.
|
|
28
|
+
writeWord(imoddata, mod.sourceEnum);
|
|
29
29
|
writeWord(imoddata, mod.modulatorDestination);
|
|
30
30
|
writeWord(imoddata, mod.transformAmount);
|
|
31
|
-
writeWord(imoddata, mod.
|
|
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.
|
|
28
|
+
writeWord(pmoddata, mod.sourceEnum);
|
|
29
29
|
writeWord(pmoddata, mod.modulatorDestination);
|
|
30
30
|
writeWord(pmoddata, mod.transformAmount);
|
|
31
|
-
writeWord(pmoddata, mod.
|
|
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
|
-
|
|
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,
|
|
@@ -44,21 +44,21 @@ export class Modulator{
|
|
|
44
44
|
constructor(dataArray) {
|
|
45
45
|
if(dataArray.srcEnum)
|
|
46
46
|
{
|
|
47
|
-
this.
|
|
47
|
+
this.sourceEnum = dataArray.srcEnum;
|
|
48
48
|
/**
|
|
49
49
|
* @type {generatorTypes}
|
|
50
50
|
*/
|
|
51
51
|
this.modulatorDestination = dataArray.dest;
|
|
52
|
-
this.
|
|
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.
|
|
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.
|
|
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.
|
|
72
|
-
this.sourceDirection = this.
|
|
73
|
-
this.sourceUsesCC = this.
|
|
74
|
-
this.sourceIndex = this.
|
|
75
|
-
this.sourceCurveType = this.
|
|
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.
|
|
79
|
-
this.secSrcDirection = this.
|
|
80
|
-
this.secSrcUsesCC = this.
|
|
81
|
-
this.secSrcIndex = this.
|
|
82
|
-
this.secSrcCurveType = this.
|
|
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.
|
|
96
|
-
secSrcEnum: this.
|
|
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:
|
|
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:
|
|
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
|
|
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,
|