spessasynth_lib 3.22.7 → 3.22.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/soundfont/basic_soundfont/generator.js +1 -1
- package/soundfont/dls/articulator_converter.js +60 -4
- package/soundfont/dls/read_articulation.js +0 -42
- package/soundfont/dls/read_samples.js +119 -40
- package/synthetizer/worklet_processor.min.js +10 -10
- package/synthetizer/worklet_system/worklet_utilities/modulation_envelope.js +0 -0
package/package.json
CHANGED
|
@@ -106,7 +106,7 @@ generatorLimits[generatorTypes.freqVibLFO] = { min: -16000, max: 4500, def: 0 };
|
|
|
106
106
|
generatorLimits[generatorTypes.delayModEnv] = { min: -32768, max: 5000, def: -32768 }; // -32768 indicates instant phase, this is done to prevent click at the start of filter modenv
|
|
107
107
|
generatorLimits[generatorTypes.attackModEnv] = { min: -32768, max: 8000, def: -32768 };
|
|
108
108
|
generatorLimits[generatorTypes.holdModEnv] = { min: -12000, max: 5000, def: -12000 };
|
|
109
|
-
generatorLimits[generatorTypes.decayModEnv] = { min: -
|
|
109
|
+
generatorLimits[generatorTypes.decayModEnv] = { min: -12000, max: 8000, def: -12000 };
|
|
110
110
|
generatorLimits[generatorTypes.sustainModEnv] = { min: 0, max: 1000, def: 0 };
|
|
111
111
|
generatorLimits[generatorTypes.releaseModEnv] = { min: -7200, max: 8000, def: -12000 }; // min is set to -7200 to prevent lowpass clicks
|
|
112
112
|
// keynum to mod env
|
|
@@ -4,6 +4,7 @@ import { midiControllers } from "../../midi_parser/midi_message.js";
|
|
|
4
4
|
import { DLSDestinations } from "./dls_destinations.js";
|
|
5
5
|
|
|
6
6
|
import { generatorTypes } from "../basic_soundfont/generator.js";
|
|
7
|
+
import { consoleColors } from "../../utils/other.js";
|
|
7
8
|
|
|
8
9
|
/**
|
|
9
10
|
* @param source {number}
|
|
@@ -174,6 +175,48 @@ function checkForSpecialDLSCombo(source, destination)
|
|
|
174
175
|
}
|
|
175
176
|
}
|
|
176
177
|
|
|
178
|
+
/**
|
|
179
|
+
* @param source {number}
|
|
180
|
+
* @param control {number}
|
|
181
|
+
* @param destination {number}
|
|
182
|
+
* @param value {number}
|
|
183
|
+
* @param transform {number}
|
|
184
|
+
*/
|
|
185
|
+
function modulatorConverterDebug(
|
|
186
|
+
source,
|
|
187
|
+
control,
|
|
188
|
+
destination,
|
|
189
|
+
value,
|
|
190
|
+
transform
|
|
191
|
+
)
|
|
192
|
+
{
|
|
193
|
+
const type = Object.keys(DLSDestinations).find(k => DLSDestinations[k] === destination);
|
|
194
|
+
const srcType = Object.keys(DLSSources).find(k => DLSSources[k] === source);
|
|
195
|
+
const ctrlType = Object.keys(DLSSources).find(k => DLSSources[k] === control);
|
|
196
|
+
const typeString = type ? type : destination.toString(16);
|
|
197
|
+
const srcString = srcType ? srcType : source.toString(16);
|
|
198
|
+
const ctrlString = ctrlType ? ctrlType : control.toString(16);
|
|
199
|
+
console.debug(
|
|
200
|
+
`%cAttempting to convert the following DLS Articulator to SF2 Modulator:
|
|
201
|
+
Source: %c${srcString}%c
|
|
202
|
+
Control: %c${ctrlString}%c
|
|
203
|
+
Destination: %c${typeString}%c
|
|
204
|
+
Amount: %c${value}%c
|
|
205
|
+
Transform: %c${transform}%c...`,
|
|
206
|
+
consoleColors.info,
|
|
207
|
+
consoleColors.recognized,
|
|
208
|
+
consoleColors.info,
|
|
209
|
+
consoleColors.recognized,
|
|
210
|
+
consoleColors.info,
|
|
211
|
+
consoleColors.recognized,
|
|
212
|
+
consoleColors.info,
|
|
213
|
+
consoleColors.recognized,
|
|
214
|
+
consoleColors.info,
|
|
215
|
+
consoleColors.recognized,
|
|
216
|
+
consoleColors.info
|
|
217
|
+
);
|
|
218
|
+
}
|
|
219
|
+
|
|
177
220
|
/**
|
|
178
221
|
* @param source {number}
|
|
179
222
|
* @param control {number}
|
|
@@ -202,6 +245,7 @@ export function getSF2ModulatorFromArticulator(
|
|
|
202
245
|
let sf2Source;
|
|
203
246
|
let swapSources = false;
|
|
204
247
|
let isSourceNoController = false;
|
|
248
|
+
let newValue = value;
|
|
205
249
|
if (specialDestination === undefined)
|
|
206
250
|
{
|
|
207
251
|
// determine destination
|
|
@@ -217,7 +261,7 @@ export function getSF2ModulatorFromArticulator(
|
|
|
217
261
|
destinationGenerator = sf2GenDestination;
|
|
218
262
|
if (sf2GenDestination.newAmount !== undefined)
|
|
219
263
|
{
|
|
220
|
-
|
|
264
|
+
newValue = sf2GenDestination.newAmount;
|
|
221
265
|
destinationGenerator = sf2GenDestination.gen;
|
|
222
266
|
}
|
|
223
267
|
sf2Source = getSF2SourceFromDLS(source);
|
|
@@ -263,10 +307,22 @@ export function getSF2ModulatorFromArticulator(
|
|
|
263
307
|
}
|
|
264
308
|
const sourceIsBipolar = (transform >> 14) & 1;
|
|
265
309
|
let sourceIsNegative = (transform >> 15) & 1;
|
|
266
|
-
// special case: for attenuation, invert source
|
|
310
|
+
// special case: for attenuation, invert source (dls gain is the opposite of sf2 attenuation)
|
|
267
311
|
if (destinationGenerator === generatorTypes.initialAttenuation)
|
|
268
312
|
{
|
|
269
|
-
|
|
313
|
+
// modulatorConverterDebug(
|
|
314
|
+
// source,
|
|
315
|
+
// control,
|
|
316
|
+
// destination,
|
|
317
|
+
// value,
|
|
318
|
+
// transform
|
|
319
|
+
// );
|
|
320
|
+
// invert only if the actual transform value is not negative.
|
|
321
|
+
// if it is, it's already inverted
|
|
322
|
+
if (value > 0)
|
|
323
|
+
{
|
|
324
|
+
sourceIsNegative = !sourceIsNegative;
|
|
325
|
+
}
|
|
270
326
|
}
|
|
271
327
|
sourceEnumFinal = getModSourceEnum(
|
|
272
328
|
sourceTransform,
|
|
@@ -301,7 +357,7 @@ export function getSF2ModulatorFromArticulator(
|
|
|
301
357
|
secSrcEnum: secSourceEnumFinal,
|
|
302
358
|
dest: destinationGenerator,
|
|
303
359
|
transform: 0x0,
|
|
304
|
-
amt:
|
|
360
|
+
amt: newValue
|
|
305
361
|
});
|
|
306
362
|
|
|
307
363
|
}
|
|
@@ -7,47 +7,6 @@ import { consoleColors } from "../../utils/other.js";
|
|
|
7
7
|
import { Generator, generatorTypes } from "../basic_soundfont/generator.js";
|
|
8
8
|
import { Modulator } from "../basic_soundfont/modulator.js";
|
|
9
9
|
|
|
10
|
-
/**
|
|
11
|
-
* @param source {number}
|
|
12
|
-
* @param control {number}
|
|
13
|
-
* @param destination {number}
|
|
14
|
-
* @param value {number}
|
|
15
|
-
* @param transform {number}
|
|
16
|
-
*/
|
|
17
|
-
function modulatorConverterDebug(
|
|
18
|
-
source,
|
|
19
|
-
control,
|
|
20
|
-
destination,
|
|
21
|
-
value,
|
|
22
|
-
transform
|
|
23
|
-
)
|
|
24
|
-
{
|
|
25
|
-
const type = Object.keys(DLSDestinations).find(k => DLSDestinations[k] === destination);
|
|
26
|
-
const srcType = Object.keys(DLSSources).find(k => DLSSources[k] === source);
|
|
27
|
-
const ctrlType = Object.keys(DLSSources).find(k => DLSSources[k] === control);
|
|
28
|
-
const typeString = type ? type : destination.toString(16);
|
|
29
|
-
const srcString = srcType ? srcType : source.toString(16);
|
|
30
|
-
const ctrlString = ctrlType ? ctrlType : control.toString(16);
|
|
31
|
-
SpessaSynthInfo(
|
|
32
|
-
`%cAttempting to convert the following DLS Articulator to SF2 Modulator:
|
|
33
|
-
Source: %c${srcString}%c
|
|
34
|
-
Control: %c${ctrlString}%c
|
|
35
|
-
Destination: %c${typeString}%c
|
|
36
|
-
Amount: %c${value}%c
|
|
37
|
-
Transform: %c${transform}%c...`,
|
|
38
|
-
consoleColors.info,
|
|
39
|
-
consoleColors.recognized,
|
|
40
|
-
consoleColors.info,
|
|
41
|
-
consoleColors.recognized,
|
|
42
|
-
consoleColors.info,
|
|
43
|
-
consoleColors.recognized,
|
|
44
|
-
consoleColors.info,
|
|
45
|
-
consoleColors.recognized,
|
|
46
|
-
consoleColors.info,
|
|
47
|
-
consoleColors.recognized,
|
|
48
|
-
consoleColors.info
|
|
49
|
-
);
|
|
50
|
-
}
|
|
51
10
|
|
|
52
11
|
/**
|
|
53
12
|
* Reads the articulator chunk
|
|
@@ -80,7 +39,6 @@ export function readArticulation(chunk, disableVibrato)
|
|
|
80
39
|
const scale = readLittleEndian(artData, 4) | 0;
|
|
81
40
|
const value = scale >> 16; // convert it to 16 bit as soundfont uses that
|
|
82
41
|
|
|
83
|
-
// modulatorConverterDebug(source, control, destination, value, transform);
|
|
84
42
|
// interpret this somehow...
|
|
85
43
|
// if source and control are both zero, it's a generator
|
|
86
44
|
if (source === 0 && control === 0 && transform === 0)
|
|
@@ -10,6 +10,102 @@ import { consoleColors } from "../../utils/other.js";
|
|
|
10
10
|
import { readLittleEndian, signedInt16 } from "../../utils/byte_functions/little_endian.js";
|
|
11
11
|
import { DLSSample } from "./dls_sample.js";
|
|
12
12
|
|
|
13
|
+
const W_FORMAT_TAG = {
|
|
14
|
+
PCM: 0x01,
|
|
15
|
+
ALAW: 0x6
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* @param dataChunk {RiffChunk}
|
|
20
|
+
* @param bytesPerSample {number}
|
|
21
|
+
* @returns {Float32Array}
|
|
22
|
+
*/
|
|
23
|
+
function readPCM(dataChunk, bytesPerSample)
|
|
24
|
+
{
|
|
25
|
+
const maxSampleValue = Math.pow(2, bytesPerSample * 8 - 1); // Max value for the sample
|
|
26
|
+
const maxUnsigned = Math.pow(2, bytesPerSample * 8);
|
|
27
|
+
|
|
28
|
+
let normalizationFactor;
|
|
29
|
+
let isUnsigned = false;
|
|
30
|
+
|
|
31
|
+
if (bytesPerSample === 1)
|
|
32
|
+
{
|
|
33
|
+
normalizationFactor = 255; // For 8-bit normalize from 0-255
|
|
34
|
+
isUnsigned = true;
|
|
35
|
+
}
|
|
36
|
+
else
|
|
37
|
+
{
|
|
38
|
+
normalizationFactor = maxSampleValue; // For 16-bit normalize from -32768 to 32767
|
|
39
|
+
}
|
|
40
|
+
const sampleLength = dataChunk.size / bytesPerSample;
|
|
41
|
+
const sampleData = new Float32Array(sampleLength);
|
|
42
|
+
for (let i = 0; i < sampleData.length; i++)
|
|
43
|
+
{
|
|
44
|
+
// read
|
|
45
|
+
let sample = readLittleEndian(dataChunk.chunkData, bytesPerSample);
|
|
46
|
+
// turn into signed
|
|
47
|
+
if (isUnsigned)
|
|
48
|
+
{
|
|
49
|
+
// normalize unsigned 8-bit sample
|
|
50
|
+
sampleData[i] = (sample / normalizationFactor) - 0.5;
|
|
51
|
+
}
|
|
52
|
+
else
|
|
53
|
+
{
|
|
54
|
+
// normalize signed 16-bit sample
|
|
55
|
+
if (sample >= maxSampleValue)
|
|
56
|
+
{
|
|
57
|
+
sample -= maxUnsigned;
|
|
58
|
+
}
|
|
59
|
+
sampleData[i] = sample / normalizationFactor;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
return sampleData;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* @param dataChunk {RiffChunk}
|
|
67
|
+
* @param bytesPerSample {number}
|
|
68
|
+
* @returns {Float32Array}
|
|
69
|
+
*/
|
|
70
|
+
function readALAW(dataChunk, bytesPerSample)
|
|
71
|
+
{
|
|
72
|
+
const sampleLength = dataChunk.size / bytesPerSample;
|
|
73
|
+
const sampleData = new Float32Array(sampleLength);
|
|
74
|
+
for (let i = 0; i < sampleData.length; i++)
|
|
75
|
+
{
|
|
76
|
+
// read
|
|
77
|
+
const input = readLittleEndian(dataChunk.chunkData, bytesPerSample);
|
|
78
|
+
|
|
79
|
+
// https://en.wikipedia.org/wiki/G.711#A-law
|
|
80
|
+
// re-toggle toggled bits
|
|
81
|
+
let sample = input ^ 0x55;
|
|
82
|
+
|
|
83
|
+
// remove sign bit
|
|
84
|
+
sample &= 0x7F;
|
|
85
|
+
|
|
86
|
+
// extract exponent
|
|
87
|
+
let exponent = sample >> 4;
|
|
88
|
+
// extract mantissa
|
|
89
|
+
let mantissa = sample & 0xF;
|
|
90
|
+
if (exponent > 0)
|
|
91
|
+
{
|
|
92
|
+
mantissa += 16; // add leading '1', if exponent > 0
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
mantissa = (mantissa << 4) + 0x8;
|
|
96
|
+
if (exponent > 1)
|
|
97
|
+
{
|
|
98
|
+
mantissa = mantissa << (exponent - 1);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
const s16sample = input > 127 ? mantissa : -mantissa;
|
|
102
|
+
|
|
103
|
+
// convert to float
|
|
104
|
+
sampleData[i] = s16sample / 32678;
|
|
105
|
+
}
|
|
106
|
+
return sampleData;
|
|
107
|
+
}
|
|
108
|
+
|
|
13
109
|
/**
|
|
14
110
|
* @this {DLSSoundFont}
|
|
15
111
|
* @param waveListChunk {RiffChunk}
|
|
@@ -43,10 +139,6 @@ export function readDLSSamples(waveListChunk)
|
|
|
43
139
|
}
|
|
44
140
|
// https://github.com/tpn/winsdk-10/blob/9b69fd26ac0c7d0b83d378dba01080e93349c2ed/Include/10.0.14393.0/shared/mmreg.h#L2108
|
|
45
141
|
const waveFormat = readLittleEndian(fmtChunk.chunkData, 2);
|
|
46
|
-
if (waveFormat !== 1)
|
|
47
|
-
{
|
|
48
|
-
throw new Error(`Only PCM format in WAVE is supported. Fmt reports ${waveFormat}`);
|
|
49
|
-
}
|
|
50
142
|
const channelsAmount = readLittleEndian(fmtChunk.chunkData, 2);
|
|
51
143
|
if (channelsAmount !== 1)
|
|
52
144
|
{
|
|
@@ -61,48 +153,30 @@ export function readDLSSamples(waveListChunk)
|
|
|
61
153
|
const wBitsPerSample = readLittleEndian(fmtChunk.chunkData, 2);
|
|
62
154
|
const bytesPerSample = wBitsPerSample / 8;
|
|
63
155
|
|
|
64
|
-
const maxSampleValue = Math.pow(2, bytesPerSample * 8 - 1); // Max value for the sample
|
|
65
|
-
const maxUnsigned = Math.pow(2, bytesPerSample * 8);
|
|
66
|
-
|
|
67
|
-
let normalizationFactor;
|
|
68
|
-
let isUnsigned = false;
|
|
69
|
-
|
|
70
|
-
if (wBitsPerSample === 8)
|
|
71
|
-
{
|
|
72
|
-
normalizationFactor = 255; // For 8-bit normalize from 0-255
|
|
73
|
-
isUnsigned = true;
|
|
74
|
-
}
|
|
75
|
-
else
|
|
76
|
-
{
|
|
77
|
-
normalizationFactor = maxSampleValue; // For 16-bit normalize from -32768 to 32767
|
|
78
|
-
}
|
|
79
156
|
// read the data
|
|
157
|
+
let failed = false;
|
|
80
158
|
const dataChunk = waveChunks.find(c => c.header === "data");
|
|
81
159
|
if (!dataChunk)
|
|
82
160
|
{
|
|
83
161
|
this.parsingError("No data chunk in the WAVE chunk!");
|
|
84
162
|
}
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
for (let i = 0; i < sampleData.length; i++)
|
|
163
|
+
let sampleData;
|
|
164
|
+
switch (waveFormat)
|
|
88
165
|
{
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
{
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
}
|
|
104
|
-
sampleData[i] = sample / normalizationFactor;
|
|
105
|
-
}
|
|
166
|
+
default:
|
|
167
|
+
failed = true;
|
|
168
|
+
sampleData = new Float32Array(dataChunk.size / bytesPerSample);
|
|
169
|
+
break;
|
|
170
|
+
//throw new Error(`Unsupported WAVE format. Fmt reports ${waveFormat}`);
|
|
171
|
+
|
|
172
|
+
case W_FORMAT_TAG.PCM:
|
|
173
|
+
sampleData = readPCM(dataChunk, bytesPerSample);
|
|
174
|
+
break;
|
|
175
|
+
|
|
176
|
+
case W_FORMAT_TAG.ALAW:
|
|
177
|
+
sampleData = readALAW(dataChunk, bytesPerSample);
|
|
178
|
+
break;
|
|
179
|
+
|
|
106
180
|
}
|
|
107
181
|
|
|
108
182
|
// sane defaults
|
|
@@ -162,13 +236,18 @@ export function readDLSSamples(waveListChunk)
|
|
|
162
236
|
}
|
|
163
237
|
}
|
|
164
238
|
|
|
239
|
+
if (failed)
|
|
240
|
+
{
|
|
241
|
+
console.error(`Failed to load '${sampleName}': Unsupported format: (${waveFormat})`);
|
|
242
|
+
}
|
|
243
|
+
|
|
165
244
|
this.samples.push(new DLSSample(
|
|
166
245
|
sampleName,
|
|
167
246
|
sampleRate,
|
|
168
247
|
sampleKey,
|
|
169
248
|
samplePitch,
|
|
170
249
|
sampleLoopStart,
|
|
171
|
-
|
|
250
|
+
sampleData.length,
|
|
172
251
|
sampleData,
|
|
173
252
|
sampleDbAttenuation
|
|
174
253
|
));
|