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.
- 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/dls/dls_zone.d.ts +2 -1
- package/@types/soundfont/read_sf2/modulators.d.ts +13 -2
- package/@types/soundfont/read_sf2/presets.d.ts +4 -2
- package/package.json +1 -1
- package/soundfont/basic_soundfont/basic_preset.js +16 -19
- package/soundfont/basic_soundfont/basic_soundfont.js +8 -1
- 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_zone.js +14 -0
- package/soundfont/dls/read_region.js +9 -4
- 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 +9 -9
- 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';
|
|
@@ -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
|
-
|
|
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
|
@@ -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);
|
|
@@ -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;
|
|
@@ -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
|
-
|
|
61
|
-
|
|
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.
|
|
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,
|