spessasynth_lib 3.20.32 → 3.20.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.
|
@@ -176,7 +176,7 @@ export class WorkletVolumeEnvelope
|
|
|
176
176
|
{
|
|
177
177
|
env.attenuation = env.attenuationTarget;
|
|
178
178
|
}
|
|
179
|
-
const sustainDb = Math.min(DB_SILENCE, env.sustainDbRelative
|
|
179
|
+
const sustainDb = Math.min(DB_SILENCE, env.sustainDbRelative);
|
|
180
180
|
|
|
181
181
|
// calculate durations
|
|
182
182
|
env.attackDuration = timecentsToSamples(voice.modulatedGenerators[generatorTypes.attackVolEnv]);
|
|
@@ -186,7 +186,7 @@ export class WorkletVolumeEnvelope
|
|
|
186
186
|
// (changing from attenuation to sustain instead of -100dB)
|
|
187
187
|
const fullChange = voice.modulatedGenerators[generatorTypes.decayVolEnv];
|
|
188
188
|
const keyNumAddition = (60 - voice.targetKey) * voice.modulatedGenerators[generatorTypes.keyNumToVolEnvDecay];
|
|
189
|
-
const fraction =
|
|
189
|
+
const fraction = sustainDb / DB_SILENCE;
|
|
190
190
|
env.decayDuration = timecentsToSamples(fullChange + keyNumAddition) * fraction;
|
|
191
191
|
|
|
192
192
|
env.releaseDuration = timecentsToSamples(voice.modulatedGenerators[generatorTypes.releaseVolEnv]);
|
|
@@ -206,7 +206,7 @@ export class WorkletVolumeEnvelope
|
|
|
206
206
|
// if this is the first recalculation and the voice has no attack or delay time, set current db to peak
|
|
207
207
|
if(env.state === 0 && env.attackEnd === 0)
|
|
208
208
|
{
|
|
209
|
-
env.currentAttenuationDb = env.attenuationTarget;
|
|
209
|
+
// env.currentAttenuationDb = env.attenuationTarget;
|
|
210
210
|
env.state = 2;
|
|
211
211
|
}
|
|
212
212
|
|
|
@@ -214,9 +214,9 @@ export class WorkletVolumeEnvelope
|
|
|
214
214
|
if(voice.isInRelease)
|
|
215
215
|
{
|
|
216
216
|
// no interpolation this time: force update to actual attenuation and calculate release start from there
|
|
217
|
-
env.attenuation = Math.min(DB_SILENCE, env.attenuationTarget);
|
|
218
|
-
const sustainDb = Math.max(0, Math.min(DB_SILENCE, env.sustainDbRelative
|
|
219
|
-
const fraction =
|
|
217
|
+
//env.attenuation = Math.min(DB_SILENCE, env.attenuationTarget);
|
|
218
|
+
const sustainDb = Math.max(0, Math.min(DB_SILENCE, env.sustainDbRelative));
|
|
219
|
+
const fraction = sustainDb / DB_SILENCE;
|
|
220
220
|
env.decayDuration = timecentsToSamples(fullChange + keyNumAddition) * fraction;
|
|
221
221
|
|
|
222
222
|
switch (env.state)
|
|
@@ -232,26 +232,21 @@ export class WorkletVolumeEnvelope
|
|
|
232
232
|
// attack is linear (in gain) so we need to do get db from that
|
|
233
233
|
let elapsed = 1 - ((env.attackEnd - env.releaseStartTimeSamples) / env.attackDuration);
|
|
234
234
|
// calculate the gain that the attack would have
|
|
235
|
-
let attackGain = elapsed * decibelAttenuationToGain(env.attenuation);
|
|
236
|
-
|
|
237
235
|
// turn that into db
|
|
238
|
-
env.releaseStartDb = 20 * Math.log10(
|
|
236
|
+
env.releaseStartDb = 20 * Math.log10(elapsed) * -1;
|
|
239
237
|
break;
|
|
240
238
|
|
|
241
239
|
case 2:
|
|
242
|
-
env.releaseStartDb =
|
|
240
|
+
env.releaseStartDb = 0;
|
|
243
241
|
break;
|
|
244
242
|
|
|
245
243
|
case 3:
|
|
246
|
-
env.releaseStartDb = (1 - (env.decayEnd - env.releaseStartTimeSamples) / env.decayDuration) *
|
|
244
|
+
env.releaseStartDb = (1 - (env.decayEnd - env.releaseStartTimeSamples) / env.decayDuration) * sustainDb;
|
|
247
245
|
break;
|
|
248
246
|
|
|
249
247
|
case 4:
|
|
250
248
|
env.releaseStartDb = sustainDb;
|
|
251
249
|
break;
|
|
252
|
-
|
|
253
|
-
default:
|
|
254
|
-
env.releaseStartDb = env.currentAttenuationDb;
|
|
255
250
|
}
|
|
256
251
|
env.releaseStartDb = Math.max(0, Math.min(env.releaseStartDb, DB_SILENCE));
|
|
257
252
|
if(env.releaseStartDb >= PERCEIVED_DB_SILENCE)
|
|
@@ -268,6 +263,7 @@ export class WorkletVolumeEnvelope
|
|
|
268
263
|
* @param audioBuffer {Float32Array} the audio buffer to modify
|
|
269
264
|
* @param centibelOffset {number} the centibel offset of volume, for modLFOtoVolume
|
|
270
265
|
* @param smoothingFactor {number} the adjusted smoothing factor for the envelope
|
|
266
|
+
* @description essentially we use approach of 100dB is silence, 0dB is peak, and always add attenuation to that (which is interpolated)
|
|
271
267
|
*/
|
|
272
268
|
static apply(voice, audioBuffer, centibelOffset, smoothingFactor)
|
|
273
269
|
{
|
|
@@ -279,9 +275,6 @@ export class WorkletVolumeEnvelope
|
|
|
279
275
|
// RELEASE PHASE
|
|
280
276
|
if(voice.isInRelease)
|
|
281
277
|
{
|
|
282
|
-
// release needs a more aggressive smoothing factor
|
|
283
|
-
// as the instant notes don't end instantly when they should
|
|
284
|
-
const releaseSmoothingFactor = smoothingFactor * 10;
|
|
285
278
|
let elapsedRelease = env.currentSampleTime - env.releaseStartTimeSamples;
|
|
286
279
|
if(elapsedRelease >= env.releaseDuration)
|
|
287
280
|
{
|
|
@@ -295,9 +288,10 @@ export class WorkletVolumeEnvelope
|
|
|
295
288
|
let dbDifference = DB_SILENCE - env.releaseStartDb;
|
|
296
289
|
for (let i = 0; i < audioBuffer.length; i++)
|
|
297
290
|
{
|
|
291
|
+
// attenuation interpolation
|
|
292
|
+
env.attenuation += (env.attenuationTarget - env.attenuation) * attenuationSmoothing;
|
|
298
293
|
let db = (elapsedRelease / env.releaseDuration) * dbDifference + env.releaseStartDb;
|
|
299
|
-
|
|
300
|
-
env.currentReleaseGain += (gain - env.currentReleaseGain) * releaseSmoothingFactor;
|
|
294
|
+
env.currentReleaseGain = decibelAttenuationToGain(db + decibelOffset + env.attenuation);
|
|
301
295
|
audioBuffer[i] *= env.currentReleaseGain;
|
|
302
296
|
env.currentSampleTime++;
|
|
303
297
|
elapsedRelease++;
|
|
@@ -330,7 +324,6 @@ export class WorkletVolumeEnvelope
|
|
|
330
324
|
// fallthrough
|
|
331
325
|
|
|
332
326
|
case 1:
|
|
333
|
-
let gain;
|
|
334
327
|
// attack phase: ramp from 0 to attenuation
|
|
335
328
|
while(env.currentSampleTime < env.attackEnd)
|
|
336
329
|
{
|
|
@@ -339,10 +332,9 @@ export class WorkletVolumeEnvelope
|
|
|
339
332
|
|
|
340
333
|
// Special case: linear gain ramp instead of linear db ramp
|
|
341
334
|
let linearAttenuation = 1 - (env.attackEnd - env.currentSampleTime) / env.attackDuration; // 0 to 1
|
|
342
|
-
|
|
343
|
-
audioBuffer[filledBuffer] *= gain;
|
|
335
|
+
audioBuffer[filledBuffer] *= linearAttenuation * decibelAttenuationToGain(env.attenuation + decibelOffset);
|
|
344
336
|
// set current attenuation to peak as its invalid during this phase
|
|
345
|
-
env.currentAttenuationDb =
|
|
337
|
+
env.currentAttenuationDb = 0;
|
|
346
338
|
|
|
347
339
|
env.currentSampleTime++;
|
|
348
340
|
if(++filledBuffer >= audioBuffer.length)
|
|
@@ -361,7 +353,7 @@ export class WorkletVolumeEnvelope
|
|
|
361
353
|
env.attenuation += (env.attenuationTarget - env.attenuation) * attenuationSmoothing;
|
|
362
354
|
|
|
363
355
|
audioBuffer[filledBuffer] *= decibelAttenuationToGain(env.attenuation + decibelOffset);
|
|
364
|
-
env.currentAttenuationDb =
|
|
356
|
+
env.currentAttenuationDb = 0;
|
|
365
357
|
|
|
366
358
|
env.currentSampleTime++;
|
|
367
359
|
if(++filledBuffer >= audioBuffer.length)
|
|
@@ -379,9 +371,8 @@ export class WorkletVolumeEnvelope
|
|
|
379
371
|
// attenuation interpolation
|
|
380
372
|
env.attenuation += (env.attenuationTarget - env.attenuation) * attenuationSmoothing;
|
|
381
373
|
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
audioBuffer[filledBuffer] *= decibelAttenuationToGain(env.currentAttenuationDb + decibelOffset);
|
|
374
|
+
env.currentAttenuationDb = (1 - (env.decayEnd - env.currentSampleTime) / env.decayDuration) * env.sustainDbRelative;
|
|
375
|
+
audioBuffer[filledBuffer] *= decibelAttenuationToGain(env.currentAttenuationDb + decibelOffset + env.attenuation);
|
|
385
376
|
|
|
386
377
|
env.currentSampleTime++;
|
|
387
378
|
if(++filledBuffer >= audioBuffer.length)
|
|
@@ -402,10 +393,9 @@ export class WorkletVolumeEnvelope
|
|
|
402
393
|
{
|
|
403
394
|
// attenuation interpolation
|
|
404
395
|
env.attenuation += (env.attenuationTarget - env.attenuation) * attenuationSmoothing;
|
|
405
|
-
const sustainDb = Math.min(DB_SILENCE, env.sustainDbRelative + env.attenuation);
|
|
406
396
|
|
|
407
|
-
audioBuffer[filledBuffer] *= decibelAttenuationToGain(
|
|
408
|
-
env.currentAttenuationDb =
|
|
397
|
+
audioBuffer[filledBuffer] *= decibelAttenuationToGain(env.sustainDbRelative + decibelOffset + env.attenuation);
|
|
398
|
+
env.currentAttenuationDb = env.sustainDbRelative;
|
|
409
399
|
env.currentSampleTime++;
|
|
410
400
|
if(++filledBuffer >= audioBuffer.length)
|
|
411
401
|
{
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { modulatorSources } from '../../../soundfont/read_sf2/modulators.js'
|
|
2
2
|
import { getModulatorCurveValue, MOD_PRECOMPUTED_LENGTH } from './modulator_curves.js'
|
|
3
3
|
import { NON_CC_INDEX_OFFSET } from './worklet_processor_channel.js'
|
|
4
|
-
import { generatorTypes } from '../../../soundfont/read_sf2/generators.js'
|
|
4
|
+
import { generatorLimits, generatorTypes } from '../../../soundfont/read_sf2/generators.js'
|
|
5
5
|
import { WorkletVolumeEnvelope } from './volume_envelope.js'
|
|
6
6
|
import { WorkletModulationEnvelope } from './modulation_envelope.js'
|
|
7
7
|
|
|
@@ -123,7 +123,9 @@ export function computeModulators(voice, controllerTable, sourceUsesCC = -1, sou
|
|
|
123
123
|
// All modulators mode: compute all modulators
|
|
124
124
|
modulatedGenerators.set(generators);
|
|
125
125
|
modulators.forEach(mod => {
|
|
126
|
-
|
|
126
|
+
const limits = generatorLimits[mod.modulatorDestination];
|
|
127
|
+
const newValue = modulatedGenerators[mod.modulatorDestination] + computeWorkletModulator(controllerTable, mod, voice);
|
|
128
|
+
modulatedGenerators[mod.modulatorDestination] = Math.max(limits.min, Math.min(newValue, limits.max));
|
|
127
129
|
});
|
|
128
130
|
WorkletVolumeEnvelope.recalculate(voice);
|
|
129
131
|
return;
|
|
@@ -158,7 +160,10 @@ export function computeModulators(voice, controllerTable, sourceUsesCC = -1, sou
|
|
|
158
160
|
modulators.forEach(m => {
|
|
159
161
|
if (m.modulatorDestination === destination)
|
|
160
162
|
{
|
|
161
|
-
|
|
163
|
+
const limits = generatorLimits[mod.modulatorDestination];
|
|
164
|
+
const current = modulatedGenerators[mod.modulatorDestination];
|
|
165
|
+
const newValue = current + computeWorkletModulator(controllerTable, m, voice);
|
|
166
|
+
modulatedGenerators[mod.modulatorDestination] = Math.max(limits.min, Math.min(newValue, limits.max));
|
|
162
167
|
}
|
|
163
168
|
});
|
|
164
169
|
computedDestinations.add(destination);
|
|
@@ -183,16 +188,16 @@ const transforms = [];
|
|
|
183
188
|
for(let curve = 0; curve < 4; curve++)
|
|
184
189
|
{
|
|
185
190
|
transforms[curve] =
|
|
186
|
-
[
|
|
187
191
|
[
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
192
|
+
[
|
|
193
|
+
new Float32Array(MOD_PRECOMPUTED_LENGTH),
|
|
194
|
+
new Float32Array(MOD_PRECOMPUTED_LENGTH)
|
|
195
|
+
],
|
|
196
|
+
[
|
|
197
|
+
new Float32Array(MOD_PRECOMPUTED_LENGTH),
|
|
198
|
+
new Float32Array(MOD_PRECOMPUTED_LENGTH)
|
|
199
|
+
]
|
|
200
|
+
];
|
|
196
201
|
for (let i = 0; i < MOD_PRECOMPUTED_LENGTH; i++) {
|
|
197
202
|
|
|
198
203
|
// polarity 0 dir 0
|