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 + env.attenuation);
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 = (sustainDb - env.attenuation) / DB_SILENCE;
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 + env.attenuation));
219
- const fraction = (sustainDb - env.attenuation) / DB_SILENCE;
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(attackGain) * -1;
236
+ env.releaseStartDb = 20 * Math.log10(elapsed) * -1;
239
237
  break;
240
238
 
241
239
  case 2:
242
- env.releaseStartDb = env.attenuation;
240
+ env.releaseStartDb = 0;
243
241
  break;
244
242
 
245
243
  case 3:
246
- env.releaseStartDb = (1 - (env.decayEnd - env.releaseStartTimeSamples) / env.decayDuration) * (sustainDb - env.attenuation) + env.attenuation;
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
- let gain = decibelAttenuationToGain(db + decibelOffset);
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
- gain = linearAttenuation * decibelAttenuationToGain(env.attenuation + decibelOffset)
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 = env.attenuation;
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 = env.attenuation;
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
- const sustainDb = Math.min(DB_SILENCE, env.sustainDbRelative + env.attenuation);
383
- env.currentAttenuationDb = (1 - (env.decayEnd - env.currentSampleTime) / env.decayDuration) * (sustainDb - env.attenuation) + env.attenuation;
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(sustainDb + decibelOffset);
408
- env.currentAttenuationDb = sustainDb;
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
- modulatedGenerators[mod.modulatorDestination] += computeWorkletModulator(controllerTable, mod, voice);
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
- modulatedGenerators[destination] += computeWorkletModulator(controllerTable, m, voice);
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
- new Float32Array(MOD_PRECOMPUTED_LENGTH),
189
- new Float32Array(MOD_PRECOMPUTED_LENGTH)
190
- ],
191
- [
192
- new Float32Array(MOD_PRECOMPUTED_LENGTH),
193
- new Float32Array(MOD_PRECOMPUTED_LENGTH)
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