Skip to content

Commit

Permalink
add linear attenuation interpolation
Browse files Browse the repository at this point in the history
fixes #81
  • Loading branch information
spessasus committed Nov 25, 2024
1 parent 082b192 commit cf7ff93
Show file tree
Hide file tree
Showing 6 changed files with 60 additions and 42 deletions.
12 changes: 6 additions & 6 deletions src/spessasynth_lib/synthetizer/worklet_processor.min.js

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,7 @@ export function noteOn(channel, midiNote, velocity, enableDebugging = false, sen
}
// set the current attenuation to target,
// as it's interpolated (we don't want 0 attenuation for even a split second)
voice.volumeEnvelope.attenuation = voice.volumeEnvelope.attenuationTarget;
voice.volumeEnvelope.attenuation = voice.volumeEnvelope.attenuationTargetGain;
// set initial pan to avoid split second changing from middle to the correct value
voice.currentPan = ((Math.max(
-500,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import { generatorTypes } from "../../../soundfont/basic_soundfont/generator.js"
* purpose: applies a volume envelope for a given voice
*/

export const VOLUME_ENVELOPE_SMOOTHING_FACTOR = 0.001;
export const VOLUME_ENVELOPE_SMOOTHING_FACTOR = 0.01;

const DB_SILENCE = 100;
const PERCEIVED_DB_SILENCE = 90;
Expand Down Expand Up @@ -77,12 +77,17 @@ export class WorkletVolumeEnvelope
*/
releaseDuration = 0;
/**
* The voice's absolute attenuation in dB
* The voice's absolute attenuation as linear gain
* @type {number}
*/
attenuation = 0;
/**
* The attenuation target, which the "attenuation" property is linearly interpolated towards
* The attenuation target, which the "attenuation" property is linearly interpolated towards (gain)
* @type {number}
*/
attenuationTargetGain = 0;
/**
* The attenuation target, which the "attenuation" property is linearly interpolated towards (dB)
* @type {number}
*/
attenuationTarget = 0;
Expand Down Expand Up @@ -155,6 +160,7 @@ export class WorkletVolumeEnvelope
0,
Math.min(voice.modulatedGenerators[generatorTypes.initialAttenuation], 1440)
) / 10; // divide by ten to get decibels
env.attenuationTargetGain = decibelAttenuationToGain(env.attenuationTarget);
env.sustainDbRelative = Math.min(DB_SILENCE, voice.modulatedGenerators[generatorTypes.sustainVolEnv] / 10);
const sustainDb = Math.min(DB_SILENCE, env.sustainDbRelative);

Expand Down Expand Up @@ -276,9 +282,9 @@ export class WorkletVolumeEnvelope
for (let i = 0; i < audioBuffer.length; i++)
{
// attenuation interpolation
env.attenuation += (env.attenuationTarget - env.attenuation) * attenuationSmoothing;
env.attenuation += (env.attenuationTargetGain - env.attenuation) * attenuationSmoothing;
let db = (elapsedRelease / env.releaseDuration) * dbDifference + env.releaseStartDb;
env.currentReleaseGain = decibelAttenuationToGain(db + decibelOffset + env.attenuation);
env.currentReleaseGain = env.attenuation * decibelAttenuationToGain(db + decibelOffset);
audioBuffer[i] *= env.currentReleaseGain;
env.currentSampleTime++;
elapsedRelease++;
Expand Down Expand Up @@ -315,11 +321,12 @@ export class WorkletVolumeEnvelope
while (env.currentSampleTime < env.attackEnd)
{
// attenuation interpolation
env.attenuation += (env.attenuationTarget - env.attenuation) * attenuationSmoothing;
env.attenuation += (env.attenuationTargetGain - env.attenuation) * attenuationSmoothing;

// Special case: linear gain ramp instead of linear db ramp
let linearAttenuation = 1 - (env.attackEnd - env.currentSampleTime) / env.attackDuration; // 0 to 1
audioBuffer[filledBuffer] *= linearAttenuation * decibelAttenuationToGain(env.attenuation + decibelOffset);
audioBuffer[filledBuffer] *= linearAttenuation * env.attenuation * decibelAttenuationToGain(
decibelOffset);
// set current attenuation to peak as its invalid during this phase
env.currentAttenuationDb = 0;

Expand All @@ -337,9 +344,9 @@ export class WorkletVolumeEnvelope
while (env.currentSampleTime < env.holdEnd)
{
// attenuation interpolation
env.attenuation += (env.attenuationTarget - env.attenuation) * attenuationSmoothing;
env.attenuation += (env.attenuationTargetGain - env.attenuation) * attenuationSmoothing;

audioBuffer[filledBuffer] *= decibelAttenuationToGain(env.attenuation + decibelOffset);
audioBuffer[filledBuffer] *= env.attenuation * decibelAttenuationToGain(decibelOffset);
env.currentAttenuationDb = 0;

env.currentSampleTime++;
Expand All @@ -356,10 +363,10 @@ export class WorkletVolumeEnvelope
while (env.currentSampleTime < env.decayEnd)
{
// attenuation interpolation
env.attenuation += (env.attenuationTarget - env.attenuation) * attenuationSmoothing;
env.attenuation += (env.attenuationTargetGain - env.attenuation) * attenuationSmoothing;

env.currentAttenuationDb = (1 - (env.decayEnd - env.currentSampleTime) / env.decayDuration) * env.sustainDbRelative;
audioBuffer[filledBuffer] *= decibelAttenuationToGain(env.currentAttenuationDb + decibelOffset + env.attenuation);
audioBuffer[filledBuffer] *= env.attenuation * decibelAttenuationToGain(env.currentAttenuationDb + decibelOffset);

env.currentSampleTime++;
if (++filledBuffer >= audioBuffer.length)
Expand All @@ -379,9 +386,9 @@ export class WorkletVolumeEnvelope
while (true)
{
// attenuation interpolation
env.attenuation += (env.attenuationTarget - env.attenuation) * attenuationSmoothing;
env.attenuation += (env.attenuationTargetGain - env.attenuation) * attenuationSmoothing;

audioBuffer[filledBuffer] *= decibelAttenuationToGain(env.sustainDbRelative + decibelOffset + env.attenuation);
audioBuffer[filledBuffer] *= env.attenuation * decibelAttenuationToGain(env.sustainDbRelative + decibelOffset);
env.currentAttenuationDb = env.sustainDbRelative;
env.currentSampleTime++;
if (++filledBuffer >= audioBuffer.length)
Expand Down
13 changes: 12 additions & 1 deletion src/website/js/synthesizer_ui/methods/key_modifier_ui.js
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,18 @@ async function doModifyKey(synth, locale, keyboard, presetList)
};
const presetOptions = {};
presetOptions["unchanged"] = locale.getLocaleString(LOCALE_PATH + "modifyKey.preset.unchanged");
for (const p of presetList)
for (const p of presetList.toSorted((p1, p2) =>
{
if (p1.presetName < p2.presetName)
{
return -1;
}
if (p1.presetName > p2.presetName)
{
return 1;
}
return 0;
}))
{
presetOptions[p.presetName] = p.presetName;
}
Expand Down
40 changes: 20 additions & 20 deletions src/website/minified/demo_main.min.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion src/website/minified/local_main.min.js

Large diffs are not rendered by default.

0 comments on commit cf7ff93

Please sign in to comment.