spessasynth_lib 3.22.12 → 3.23.0
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_soundfont.d.ts +2 -0
- package/@types/soundfont/basic_soundfont/basic_zone.d.ts +16 -0
- package/@types/soundfont/basic_soundfont/generator.d.ts +2 -1
- package/@types/soundfont/basic_soundfont/riff_chunk.d.ts +2 -1
- package/@types/soundfont/basic_soundfont/write_dls/art2.d.ts +6 -0
- package/@types/soundfont/basic_soundfont/write_dls/articulator.d.ts +28 -0
- package/@types/soundfont/basic_soundfont/write_dls/combine_zones.d.ts +8 -0
- package/@types/soundfont/basic_soundfont/write_dls/ins.d.ts +7 -0
- package/@types/soundfont/basic_soundfont/write_dls/lins.d.ts +5 -0
- package/@types/soundfont/basic_soundfont/write_dls/modulator_converter.d.ts +11 -0
- package/@types/soundfont/basic_soundfont/write_dls/rgn2.d.ts +7 -0
- package/@types/soundfont/basic_soundfont/write_dls/wave.d.ts +6 -0
- package/@types/soundfont/basic_soundfont/write_dls/write_dls.d.ts +6 -0
- package/@types/soundfont/basic_soundfont/write_dls/wsmp.d.ts +12 -0
- package/@types/soundfont/basic_soundfont/write_dls/wvpl.d.ts +8 -0
- package/midi_parser/midi_editor.js +0 -0
- package/package.json +1 -1
- package/soundfont/basic_soundfont/basic_preset.js +23 -12
- package/soundfont/basic_soundfont/basic_soundfont.js +2 -0
- package/soundfont/basic_soundfont/basic_zone.js +30 -5
- package/soundfont/basic_soundfont/generator.js +10 -4
- package/soundfont/basic_soundfont/riff_chunk.js +20 -5
- package/soundfont/basic_soundfont/write_dls/art2.js +136 -0
- package/soundfont/basic_soundfont/write_dls/articulator.js +49 -0
- package/soundfont/basic_soundfont/write_dls/combine_zones.js +398 -0
- package/soundfont/basic_soundfont/write_dls/ins.js +103 -0
- package/soundfont/basic_soundfont/write_dls/lins.js +18 -0
- package/soundfont/basic_soundfont/write_dls/modulator_converter.js +324 -0
- package/soundfont/basic_soundfont/write_dls/rgn2.js +101 -0
- package/soundfont/basic_soundfont/write_dls/wave.js +74 -0
- package/soundfont/basic_soundfont/write_dls/write_dls.js +118 -0
- package/soundfont/basic_soundfont/write_dls/wsmp.js +74 -0
- package/soundfont/basic_soundfont/write_dls/wvpl.js +32 -0
- package/soundfont/basic_soundfont/write_sf2/igen.js +3 -3
- package/soundfont/basic_soundfont/write_sf2/pgen.js +2 -2
- package/soundfont/dls/articulator_converter.js +10 -0
- package/soundfont/dls/dls_sample.js +2 -2
- package/soundfont/dls/dls_soundfont.js +2 -1
- package/soundfont/dls/dls_zone.js +21 -18
- package/soundfont/dls/read_articulation.js +18 -30
- package/soundfont/dls/read_instrument.js +31 -1
- package/soundfont/dls/read_region.js +0 -4
- package/soundfont/dls/read_samples.js +17 -17
- package/synthetizer/key_modifier_manager.js +1 -1
- package/synthetizer/worklet_processor.min.js +13 -12
- package/synthetizer/worklet_system/main_processor.js +2 -0
- package/synthetizer/worklet_system/worklet_methods/note_on.js +2 -1
- package/synthetizer/worklet_system/worklet_methods/reset_controllers.js +4 -12
- package/synthetizer/worklet_system/worklet_methods/voice_control.js +4 -3
- package/synthetizer/worklet_system/worklet_methods/worklet_soundfont_manager/worklet_soundfont_manager.js +0 -0
- package/synthetizer/worklet_system/worklet_utilities/worklet_voice.js +1 -1
- package/utils/byte_functions/string.js +1 -1
|
@@ -87,6 +87,8 @@ export class BasicSoundFont {
|
|
|
87
87
|
parsingError(error: string): void;
|
|
88
88
|
destroySoundfont(): void;
|
|
89
89
|
write: typeof write;
|
|
90
|
+
writeDLS: typeof writeDLS;
|
|
90
91
|
}
|
|
91
92
|
import { Modulator } from "./modulator.js";
|
|
92
93
|
import { write } from "./write_sf2/write.js";
|
|
94
|
+
import { writeDLS } from "./write_dls/write_dls.js";
|
|
@@ -6,11 +6,13 @@
|
|
|
6
6
|
export class BasicZone {
|
|
7
7
|
/**
|
|
8
8
|
* The zone's velocity range
|
|
9
|
+
* min -1 means that it is a default value
|
|
9
10
|
* @type {SoundFontRange}
|
|
10
11
|
*/
|
|
11
12
|
velRange: SoundFontRange;
|
|
12
13
|
/**
|
|
13
14
|
* The zone's key range
|
|
15
|
+
* min -1 means that it is a default value
|
|
14
16
|
* @type {SoundFontRange}
|
|
15
17
|
*/
|
|
16
18
|
keyRange: SoundFontRange;
|
|
@@ -29,6 +31,20 @@ export class BasicZone {
|
|
|
29
31
|
* @type {Modulator[]}
|
|
30
32
|
*/
|
|
31
33
|
modulators: Modulator[];
|
|
34
|
+
/**
|
|
35
|
+
* @returns {boolean}
|
|
36
|
+
*/
|
|
37
|
+
get hasKeyRange(): boolean;
|
|
38
|
+
/**
|
|
39
|
+
* @returns {boolean}
|
|
40
|
+
*/
|
|
41
|
+
get hasVelRange(): boolean;
|
|
42
|
+
/**
|
|
43
|
+
* @param generatorType {generatorTypes}
|
|
44
|
+
* @param notFoundValue {number}
|
|
45
|
+
* @returns {number}
|
|
46
|
+
*/
|
|
47
|
+
getGeneratorValue(generatorType: generatorTypes, notFoundValue: number): number;
|
|
32
48
|
}
|
|
33
49
|
export type SoundFontRange = {
|
|
34
50
|
/**
|
|
@@ -86,8 +86,9 @@ export class Generator {
|
|
|
86
86
|
* Constructs a new generator
|
|
87
87
|
* @param type {generatorTypes|number}
|
|
88
88
|
* @param value {number}
|
|
89
|
+
* @param validate {boolean}
|
|
89
90
|
*/
|
|
90
|
-
constructor(type?: generatorTypes | number, value?: number);
|
|
91
|
+
constructor(type?: generatorTypes | number, value?: number, validate?: boolean);
|
|
91
92
|
/**
|
|
92
93
|
* The generator's enum number
|
|
93
94
|
* @type {generatorTypes|number}
|
|
@@ -15,9 +15,10 @@ export function writeRIFFChunk(chunk: RiffChunk, prepend?: IndexedByteArray): In
|
|
|
15
15
|
* @param header {string}
|
|
16
16
|
* @param data {Uint8Array}
|
|
17
17
|
* @param addZeroByte {Boolean}
|
|
18
|
+
* @param isList {boolean}
|
|
18
19
|
* @returns {IndexedByteArray}
|
|
19
20
|
*/
|
|
20
|
-
export function writeRIFFOddSize(header: string, data: Uint8Array, addZeroByte?: boolean): IndexedByteArray;
|
|
21
|
+
export function writeRIFFOddSize(header: string, data: Uint8Array, addZeroByte?: boolean, isList?: boolean): IndexedByteArray;
|
|
21
22
|
/**
|
|
22
23
|
* @param collection {RiffChunk[]}
|
|
23
24
|
* @param type {string}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
export class Articulator {
|
|
2
|
+
constructor(source: any, control: any, destination: any, scale: any, transform: any);
|
|
3
|
+
/**
|
|
4
|
+
* @type {DLSSources}
|
|
5
|
+
*/
|
|
6
|
+
source: DLSSources;
|
|
7
|
+
/**
|
|
8
|
+
* @type {DLSSources}
|
|
9
|
+
*/
|
|
10
|
+
control: DLSSources;
|
|
11
|
+
/**
|
|
12
|
+
* @type {DLSDestinations}
|
|
13
|
+
*/
|
|
14
|
+
destination: DLSDestinations;
|
|
15
|
+
/**
|
|
16
|
+
* @type {number}
|
|
17
|
+
*/
|
|
18
|
+
scale: number;
|
|
19
|
+
/**
|
|
20
|
+
* @type {number}
|
|
21
|
+
*/
|
|
22
|
+
transform: number;
|
|
23
|
+
/**
|
|
24
|
+
* @returns {IndexedByteArray}
|
|
25
|
+
*/
|
|
26
|
+
writeArticulator(): IndexedByteArray;
|
|
27
|
+
}
|
|
28
|
+
import { IndexedByteArray } from "../../../utils/indexed_array.js";
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Combines preset zones
|
|
3
|
+
* @param preset {BasicPreset}
|
|
4
|
+
* @param globalize {boolean}
|
|
5
|
+
* @returns {BasicInstrumentZone[]}
|
|
6
|
+
*/
|
|
7
|
+
export function combineZones(preset: BasicPreset, globalize?: boolean): BasicInstrumentZone[];
|
|
8
|
+
import { BasicInstrumentZone } from "../basic_zones.js";
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @param gen {Generator}
|
|
3
|
+
* @returns {Articulator|undefined}
|
|
4
|
+
*/
|
|
5
|
+
export function getDLSArticulatorFromSf2Generator(gen: Generator): Articulator | undefined;
|
|
6
|
+
/**
|
|
7
|
+
* @param mod {Modulator}
|
|
8
|
+
* @returns {Articulator|undefined}
|
|
9
|
+
*/
|
|
10
|
+
export function getDLSArticulatorFromSf2Modulator(mod: Modulator): Articulator | undefined;
|
|
11
|
+
import { Articulator } from "./articulator.js";
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @param zone {BasicInstrumentZone}
|
|
3
|
+
* @this {BasicSoundFont}
|
|
4
|
+
* @returns {IndexedByteArray}
|
|
5
|
+
*/
|
|
6
|
+
export function writeDLSRegion(this: BasicSoundFont, zone: BasicInstrumentZone): IndexedByteArray;
|
|
7
|
+
import { IndexedByteArray } from "../../../utils/indexed_array.js";
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @param sample {BasicSample}
|
|
3
|
+
* @param rootKey {number}
|
|
4
|
+
* @param tuning {number}
|
|
5
|
+
* @param attenuationCentibels {number} CENTIBELS, NO CORRECTION
|
|
6
|
+
* @param loopStart {number}
|
|
7
|
+
* @param loopEnd {number}
|
|
8
|
+
* @param loopingMode {number}
|
|
9
|
+
* @returns {IndexedByteArray}
|
|
10
|
+
*/
|
|
11
|
+
export function writeWavesample(sample: BasicSample, rootKey: number, tuning: number, attenuationCentibels: number, loopStart: number, loopEnd: number, loopingMode: number): IndexedByteArray;
|
|
12
|
+
import { IndexedByteArray } from "../../../utils/indexed_array.js";
|
|
File without changes
|
package/package.json
CHANGED
|
@@ -148,9 +148,14 @@ export class BasicPreset
|
|
|
148
148
|
return [];
|
|
149
149
|
}
|
|
150
150
|
|
|
151
|
-
|
|
151
|
+
/**
|
|
152
|
+
* @param range {SoundFontRange}
|
|
153
|
+
* @param number {number}
|
|
154
|
+
* @returns {boolean}
|
|
155
|
+
*/
|
|
156
|
+
function isInRange(range, number)
|
|
152
157
|
{
|
|
153
|
-
return number >= min && number <= max;
|
|
158
|
+
return number >= range.min && number <= range.max;
|
|
154
159
|
}
|
|
155
160
|
|
|
156
161
|
/**
|
|
@@ -182,51 +187,57 @@ export class BasicPreset
|
|
|
182
187
|
*/
|
|
183
188
|
let globalPresetGenerators = this.presetZones[0].isGlobal ? [...this.presetZones[0].generators] : [];
|
|
184
189
|
|
|
190
|
+
/**
|
|
191
|
+
* @type {Modulator[]}
|
|
192
|
+
*/
|
|
185
193
|
let globalPresetModulators = this.presetZones[0].isGlobal ? [...this.presetZones[0].modulators] : [];
|
|
194
|
+
const globalKeyRange = this.presetZones[0].isGlobal ? this.presetZones[0].keyRange : { min: 0, max: 127 };
|
|
195
|
+
const globalVelRange = this.presetZones[0].isGlobal ? this.presetZones[0].velRange : { min: 0, max: 127 };
|
|
186
196
|
|
|
187
197
|
// find the preset zones in range
|
|
188
198
|
let presetZonesInRange = this.presetZones.filter(currentZone =>
|
|
189
199
|
(
|
|
190
200
|
isInRange(
|
|
191
|
-
currentZone.keyRange
|
|
192
|
-
currentZone.keyRange.max,
|
|
201
|
+
currentZone.hasKeyRange ? currentZone.keyRange : globalKeyRange,
|
|
193
202
|
midiNote
|
|
194
203
|
)
|
|
195
204
|
&&
|
|
196
205
|
isInRange(
|
|
197
|
-
currentZone.velRange
|
|
198
|
-
currentZone.velRange.max,
|
|
206
|
+
currentZone.hasVelRange ? currentZone.velRange : globalVelRange,
|
|
199
207
|
velocity
|
|
200
208
|
)
|
|
201
209
|
) && !currentZone.isGlobal);
|
|
202
210
|
|
|
203
211
|
presetZonesInRange.forEach(zone =>
|
|
204
212
|
{
|
|
213
|
+
// global zone is already taken into account earlier
|
|
205
214
|
if (zone.instrument.instrumentZones.length < 1)
|
|
206
215
|
{
|
|
207
216
|
return;
|
|
208
217
|
}
|
|
209
218
|
let presetGenerators = zone.generators;
|
|
210
219
|
let presetModulators = zone.modulators;
|
|
220
|
+
const firstZone = zone.instrument.instrumentZones[0];
|
|
211
221
|
/**
|
|
212
222
|
* global zone is always first, so it or nothing
|
|
213
223
|
* @type {Generator[]}
|
|
214
224
|
*/
|
|
215
|
-
let globalInstrumentGenerators =
|
|
216
|
-
let globalInstrumentModulators =
|
|
225
|
+
let globalInstrumentGenerators = firstZone.isGlobal ? [...firstZone.generators] : [];
|
|
226
|
+
let globalInstrumentModulators = firstZone.isGlobal ? [...firstZone.modulators] : [];
|
|
227
|
+
const globalKeyRange = firstZone.isGlobal ? firstZone.keyRange : { min: 0, max: 127 };
|
|
228
|
+
const globalVelRange = firstZone.isGlobal ? firstZone.velRange : { min: 0, max: 127 };
|
|
229
|
+
|
|
217
230
|
|
|
218
231
|
let instrumentZonesInRange = zone.instrument.instrumentZones
|
|
219
232
|
.filter(currentZone =>
|
|
220
233
|
(
|
|
221
234
|
isInRange(
|
|
222
|
-
currentZone.keyRange
|
|
223
|
-
currentZone.keyRange.max,
|
|
235
|
+
currentZone.hasKeyRange ? currentZone.keyRange : globalKeyRange,
|
|
224
236
|
midiNote
|
|
225
237
|
)
|
|
226
238
|
&&
|
|
227
239
|
isInRange(
|
|
228
|
-
currentZone.velRange
|
|
229
|
-
currentZone.velRange.max,
|
|
240
|
+
currentZone.hasVelRange ? currentZone.velRange : globalVelRange,
|
|
230
241
|
velocity
|
|
231
242
|
)
|
|
232
243
|
) && !currentZone.isGlobal
|
|
@@ -2,6 +2,7 @@ import { SpessaSynthWarn } from "../../utils/loggin.js";
|
|
|
2
2
|
import { consoleColors } from "../../utils/other.js";
|
|
3
3
|
import { write } from "./write_sf2/write.js";
|
|
4
4
|
import { defaultModulators, Modulator } from "./modulator.js";
|
|
5
|
+
import { writeDLS } from "./write_dls/write_dls.js";
|
|
5
6
|
|
|
6
7
|
class BasicSoundFont
|
|
7
8
|
{
|
|
@@ -243,5 +244,6 @@ class BasicSoundFont
|
|
|
243
244
|
}
|
|
244
245
|
|
|
245
246
|
BasicSoundFont.prototype.write = write;
|
|
247
|
+
BasicSoundFont.prototype.writeDLS = writeDLS;
|
|
246
248
|
|
|
247
249
|
export { BasicSoundFont };
|
|
@@ -8,32 +8,57 @@ export class BasicZone
|
|
|
8
8
|
{
|
|
9
9
|
/**
|
|
10
10
|
* The zone's velocity range
|
|
11
|
+
* min -1 means that it is a default value
|
|
11
12
|
* @type {SoundFontRange}
|
|
12
13
|
*/
|
|
13
|
-
velRange = { min:
|
|
14
|
+
velRange = { min: -1, max: 127 };
|
|
14
15
|
|
|
15
16
|
/**
|
|
16
17
|
* The zone's key range
|
|
18
|
+
* min -1 means that it is a default value
|
|
17
19
|
* @type {SoundFontRange}
|
|
18
20
|
*/
|
|
19
|
-
keyRange = { min:
|
|
20
|
-
|
|
21
|
+
keyRange = { min: -1, max: 127 };
|
|
21
22
|
/**
|
|
22
23
|
* Indicates if the zone is global
|
|
23
24
|
* @type {boolean}
|
|
24
25
|
*/
|
|
25
26
|
isGlobal = false;
|
|
26
|
-
|
|
27
27
|
/**
|
|
28
28
|
* The zone's generators
|
|
29
29
|
* @type {Generator[]}
|
|
30
30
|
*/
|
|
31
31
|
generators = [];
|
|
32
|
-
|
|
33
32
|
/**
|
|
34
33
|
* The zone's modulators
|
|
35
34
|
* @type {Modulator[]}
|
|
36
35
|
*/
|
|
37
36
|
modulators = [];
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* @returns {boolean}
|
|
40
|
+
*/
|
|
41
|
+
get hasKeyRange()
|
|
42
|
+
{
|
|
43
|
+
return this.keyRange.min !== -1;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* @returns {boolean}
|
|
48
|
+
*/
|
|
49
|
+
get hasVelRange()
|
|
50
|
+
{
|
|
51
|
+
return this.velRange.min !== -1;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* @param generatorType {generatorTypes}
|
|
56
|
+
* @param notFoundValue {number}
|
|
57
|
+
* @returns {number}
|
|
58
|
+
*/
|
|
59
|
+
getGeneratorValue(generatorType, notFoundValue)
|
|
60
|
+
{
|
|
61
|
+
return this.generators.find(g => g.generatorType === generatorType)?.generatorValue || notFoundValue;
|
|
62
|
+
}
|
|
38
63
|
}
|
|
39
64
|
|
|
@@ -137,6 +137,7 @@ generatorLimits[generatorTypes.fineTune] = { min: -12700, max: 12700, def: 0 };
|
|
|
137
137
|
generatorLimits[generatorTypes.scaleTuning] = { min: 0, max: 1200, def: 100 };
|
|
138
138
|
generatorLimits[generatorTypes.exclusiveClass] = { min: 0, max: 99999, def: 0 };
|
|
139
139
|
generatorLimits[generatorTypes.overridingRootKey] = { min: 0 - 1, max: 127, def: -1 };
|
|
140
|
+
generatorLimits[generatorTypes.sampleModes] = { min: 0, max: 3, def: 0 };
|
|
140
141
|
|
|
141
142
|
export class Generator
|
|
142
143
|
{
|
|
@@ -155,19 +156,24 @@ export class Generator
|
|
|
155
156
|
* Constructs a new generator
|
|
156
157
|
* @param type {generatorTypes|number}
|
|
157
158
|
* @param value {number}
|
|
159
|
+
* @param validate {boolean}
|
|
158
160
|
*/
|
|
159
|
-
constructor(type = generatorTypes.INVALID, value = 0)
|
|
161
|
+
constructor(type = generatorTypes.INVALID, value = 0, validate = true)
|
|
160
162
|
{
|
|
161
163
|
this.generatorType = type;
|
|
162
164
|
if (value === undefined)
|
|
163
165
|
{
|
|
164
166
|
throw new Error("No value provided.");
|
|
165
167
|
}
|
|
166
|
-
const lim = generatorLimits[type];
|
|
167
168
|
this.generatorValue = Math.round(value);
|
|
168
|
-
if (
|
|
169
|
+
if (validate)
|
|
169
170
|
{
|
|
170
|
-
|
|
171
|
+
const lim = generatorLimits[type];
|
|
172
|
+
|
|
173
|
+
if (lim !== undefined)
|
|
174
|
+
{
|
|
175
|
+
this.generatorValue = Math.max(lim.min, Math.min(lim.max, this.generatorValue));
|
|
176
|
+
}
|
|
171
177
|
}
|
|
172
178
|
}
|
|
173
179
|
}
|
|
@@ -93,9 +93,10 @@ export function writeRIFFChunk(chunk, prepend = undefined)
|
|
|
93
93
|
* @param header {string}
|
|
94
94
|
* @param data {Uint8Array}
|
|
95
95
|
* @param addZeroByte {Boolean}
|
|
96
|
+
* @param isList {boolean}
|
|
96
97
|
* @returns {IndexedByteArray}
|
|
97
98
|
*/
|
|
98
|
-
export function writeRIFFOddSize(header, data, addZeroByte = false)
|
|
99
|
+
export function writeRIFFOddSize(header, data, addZeroByte = false, isList = false)
|
|
99
100
|
{
|
|
100
101
|
if (addZeroByte)
|
|
101
102
|
{
|
|
@@ -103,15 +104,29 @@ export function writeRIFFOddSize(header, data, addZeroByte = false)
|
|
|
103
104
|
tempData.set(data, 0);
|
|
104
105
|
data = tempData;
|
|
105
106
|
}
|
|
106
|
-
let
|
|
107
|
+
let offset = 8;
|
|
108
|
+
let finalSize = offset + data.length;
|
|
109
|
+
let writtenSize = data.length;
|
|
107
110
|
if (finalSize % 2 !== 0)
|
|
108
111
|
{
|
|
109
112
|
finalSize++;
|
|
110
113
|
}
|
|
114
|
+
let headerWritten = header;
|
|
115
|
+
if (isList)
|
|
116
|
+
{
|
|
117
|
+
finalSize += 4;
|
|
118
|
+
writtenSize += 4;
|
|
119
|
+
offset += 4;
|
|
120
|
+
headerWritten = "LIST";
|
|
121
|
+
}
|
|
111
122
|
const outArray = new IndexedByteArray(finalSize);
|
|
112
|
-
writeStringAsBytes(outArray,
|
|
113
|
-
writeDword(outArray,
|
|
114
|
-
|
|
123
|
+
writeStringAsBytes(outArray, headerWritten);
|
|
124
|
+
writeDword(outArray, writtenSize);
|
|
125
|
+
if (isList)
|
|
126
|
+
{
|
|
127
|
+
writeStringAsBytes(outArray, header);
|
|
128
|
+
}
|
|
129
|
+
outArray.set(data, offset);
|
|
115
130
|
return outArray;
|
|
116
131
|
}
|
|
117
132
|
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
import { getDLSArticulatorFromSf2Generator, getDLSArticulatorFromSf2Modulator } from "./modulator_converter.js";
|
|
2
|
+
import { writeRIFFOddSize } from "../riff_chunk.js";
|
|
3
|
+
import { combineArrays, IndexedByteArray } from "../../../utils/indexed_array.js";
|
|
4
|
+
import { Generator, generatorTypes } from "../generator.js";
|
|
5
|
+
import { writeDword } from "../../../utils/byte_functions/little_endian.js";
|
|
6
|
+
import { consoleColors } from "../../../utils/other.js";
|
|
7
|
+
import { SpessaSynthInfo, SpessaSynthWarn } from "../../../utils/loggin.js";
|
|
8
|
+
|
|
9
|
+
const invalidGeneratorTypes = new Set([
|
|
10
|
+
generatorTypes.sampleModes,
|
|
11
|
+
generatorTypes.initialAttenuation,
|
|
12
|
+
generatorTypes.keyRange,
|
|
13
|
+
generatorTypes.velRange,
|
|
14
|
+
generatorTypes.sampleID,
|
|
15
|
+
generatorTypes.fineTune,
|
|
16
|
+
generatorTypes.coarseTune,
|
|
17
|
+
generatorTypes.startAddrsOffset,
|
|
18
|
+
generatorTypes.startAddrsCoarseOffset,
|
|
19
|
+
generatorTypes.endAddrOffset,
|
|
20
|
+
generatorTypes.endAddrsCoarseOffset,
|
|
21
|
+
generatorTypes.startloopAddrsOffset,
|
|
22
|
+
generatorTypes.startloopAddrsCoarseOffset,
|
|
23
|
+
generatorTypes.endloopAddrsOffset,
|
|
24
|
+
generatorTypes.endloopAddrsCoarseOffset,
|
|
25
|
+
generatorTypes.overridingRootKey,
|
|
26
|
+
generatorTypes.exclusiveClass
|
|
27
|
+
]);
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* @param zone {BasicInstrumentZone}
|
|
31
|
+
* @returns {IndexedByteArray}
|
|
32
|
+
*/
|
|
33
|
+
export function writeArticulator(zone)
|
|
34
|
+
{
|
|
35
|
+
/**
|
|
36
|
+
* @returns {number}
|
|
37
|
+
*/
|
|
38
|
+
|
|
39
|
+
// read_articulation.js:
|
|
40
|
+
// according to viena and another strange (with modulators) rendition of gm.dls in sf2,
|
|
41
|
+
// it shall be divided by -128
|
|
42
|
+
// and a strange correction needs to be applied to the real value:
|
|
43
|
+
// real + (60 / 128) * scale
|
|
44
|
+
|
|
45
|
+
// we invert this here
|
|
46
|
+
for (let i = 0; i < zone.generators.length; i++)
|
|
47
|
+
{
|
|
48
|
+
const relativeGenerator = zone.generators[i];
|
|
49
|
+
let absoluteCounterpart = undefined;
|
|
50
|
+
switch (relativeGenerator.generatorType)
|
|
51
|
+
{
|
|
52
|
+
default:
|
|
53
|
+
continue;
|
|
54
|
+
|
|
55
|
+
case generatorTypes.keyNumToVolEnvDecay:
|
|
56
|
+
absoluteCounterpart = generatorTypes.decayVolEnv;
|
|
57
|
+
break;
|
|
58
|
+
case generatorTypes.keyNumToVolEnvHold:
|
|
59
|
+
absoluteCounterpart = generatorTypes.holdVolEnv;
|
|
60
|
+
break;
|
|
61
|
+
case generatorTypes.keyNumToModEnvDecay:
|
|
62
|
+
absoluteCounterpart = generatorTypes.decayModEnv;
|
|
63
|
+
break;
|
|
64
|
+
case generatorTypes.keyNumToModEnvHold:
|
|
65
|
+
absoluteCounterpart = generatorTypes.holdModEnv;
|
|
66
|
+
}
|
|
67
|
+
let absoluteGenerator = zone.generators.find(g => g.generatorType === absoluteCounterpart);
|
|
68
|
+
if (absoluteGenerator === undefined)
|
|
69
|
+
{
|
|
70
|
+
// there's no absolute generator here.
|
|
71
|
+
continue;
|
|
72
|
+
}
|
|
73
|
+
const dlsRelative = relativeGenerator.generatorValue * -128;
|
|
74
|
+
const subtraction = (60 / 128) * dlsRelative;
|
|
75
|
+
const newAbsolute = absoluteGenerator.generatorValue - subtraction;
|
|
76
|
+
|
|
77
|
+
const iR = zone.generators.indexOf(relativeGenerator);
|
|
78
|
+
const iA = zone.generators.indexOf(absoluteGenerator);
|
|
79
|
+
zone.generators[iA] =
|
|
80
|
+
new Generator(absoluteCounterpart, newAbsolute, false);
|
|
81
|
+
zone.generators[iR] =
|
|
82
|
+
new Generator(relativeGenerator.generatorType, dlsRelative, false);
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* @type {Articulator[]}
|
|
86
|
+
*/
|
|
87
|
+
const generators = zone.generators.reduce((arrs, g) =>
|
|
88
|
+
{
|
|
89
|
+
if (invalidGeneratorTypes.has(g.generatorType))
|
|
90
|
+
{
|
|
91
|
+
return arrs;
|
|
92
|
+
}
|
|
93
|
+
const art = getDLSArticulatorFromSf2Generator(g);
|
|
94
|
+
if (art !== undefined)
|
|
95
|
+
{
|
|
96
|
+
arrs.push(art);
|
|
97
|
+
SpessaSynthInfo("%cSucceeded converting to DLS Articulator!", consoleColors.recognized);
|
|
98
|
+
|
|
99
|
+
}
|
|
100
|
+
else
|
|
101
|
+
{
|
|
102
|
+
SpessaSynthWarn("Failed converting to DLS Articulator!");
|
|
103
|
+
}
|
|
104
|
+
return arrs;
|
|
105
|
+
}, []);
|
|
106
|
+
/**
|
|
107
|
+
* @type {Articulator[]}
|
|
108
|
+
*/
|
|
109
|
+
const modulators = zone.modulators.reduce((arrs, m) =>
|
|
110
|
+
{
|
|
111
|
+
const art = getDLSArticulatorFromSf2Modulator(m);
|
|
112
|
+
if (art !== undefined)
|
|
113
|
+
{
|
|
114
|
+
arrs.push(art);
|
|
115
|
+
SpessaSynthInfo("%cSucceeded converting to DLS Articulator!", consoleColors.recognized);
|
|
116
|
+
|
|
117
|
+
}
|
|
118
|
+
else
|
|
119
|
+
{
|
|
120
|
+
SpessaSynthWarn("Failed converting to DLS Articulator!");
|
|
121
|
+
}
|
|
122
|
+
return arrs;
|
|
123
|
+
}, []);
|
|
124
|
+
generators.push(...modulators);
|
|
125
|
+
|
|
126
|
+
const art2Data = new IndexedByteArray(8);
|
|
127
|
+
writeDword(art2Data, 8); // cbSize
|
|
128
|
+
writeDword(art2Data, generators.length); // cbConnectionBlocks
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
const out = generators.map(a => a.writeArticulator());
|
|
132
|
+
return writeRIFFOddSize(
|
|
133
|
+
"art2",
|
|
134
|
+
combineArrays([art2Data, ...out])
|
|
135
|
+
);
|
|
136
|
+
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { IndexedByteArray } from "../../../utils/indexed_array.js";
|
|
2
|
+
import { writeDword, writeWord } from "../../../utils/byte_functions/little_endian.js";
|
|
3
|
+
|
|
4
|
+
export class Articulator
|
|
5
|
+
{
|
|
6
|
+
/**
|
|
7
|
+
* @type {DLSSources}
|
|
8
|
+
*/
|
|
9
|
+
source;
|
|
10
|
+
/**
|
|
11
|
+
* @type {DLSSources}
|
|
12
|
+
*/
|
|
13
|
+
control;
|
|
14
|
+
/**
|
|
15
|
+
* @type {DLSDestinations}
|
|
16
|
+
*/
|
|
17
|
+
destination;
|
|
18
|
+
/**
|
|
19
|
+
* @type {number}
|
|
20
|
+
*/
|
|
21
|
+
scale;
|
|
22
|
+
/**
|
|
23
|
+
* @type {number}
|
|
24
|
+
*/
|
|
25
|
+
transform;
|
|
26
|
+
|
|
27
|
+
constructor(source, control, destination, scale, transform)
|
|
28
|
+
{
|
|
29
|
+
this.source = source;
|
|
30
|
+
this.control = control;
|
|
31
|
+
this.destination = destination;
|
|
32
|
+
this.scale = scale;
|
|
33
|
+
this.transform = transform;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* @returns {IndexedByteArray}
|
|
38
|
+
*/
|
|
39
|
+
writeArticulator()
|
|
40
|
+
{
|
|
41
|
+
const out = new IndexedByteArray(12);
|
|
42
|
+
writeWord(out, this.source);
|
|
43
|
+
writeWord(out, this.control);
|
|
44
|
+
writeWord(out, this.destination);
|
|
45
|
+
writeWord(out, this.transform);
|
|
46
|
+
writeDword(out, this.scale << 16);
|
|
47
|
+
return out;
|
|
48
|
+
}
|
|
49
|
+
}
|