Update Ableton effect parameter mapping and refactor value calculation logic across multiple effects (Reverb, Delay, Distortion, Chorus, Filter, Compressor).

This commit is contained in:
2026-07-13 08:49:30 +02:00
parent 943876e9d1
commit af18829b95
2 changed files with 44 additions and 18 deletions

View File

@@ -978,14 +978,22 @@ async function buildReturnTrack(
if (projectData.effects.effectType === EffectType.Reverb) { if (projectData.effects.effectType === EffectType.Reverb) {
const effectReverbTemplate = await loadTemplate<ALSReverb>('effectReverb', abletonVersion); const effectReverbTemplate = await loadTemplate<ALSReverb>('effectReverb', abletonVersion);
const effectReverb = structuredClone(effectReverbTemplate.Reverb); const effectReverb = structuredClone(effectReverbTemplate.Reverb);
const values = reverbEffectValues(projectData.effects.param1, projectData.effects.param2);
// these params are very rough approximation of the KO's reverb // these params are very rough approximation of the KO's reverb
// it's not exact, but close enough // it's not exact, but close enough
effectReverb.PreDelay.Manual['@Value'] = 0.5; effectReverb.PreDelay.Manual['@Value'] = 0.5;
effectReverb.RoomSize.Manual['@Value'] = 300; effectReverb.RoomSize.Manual['@Value'] = 300;
effectReverb.ShelfHiFreq.Manual['@Value'] = 1000; effectReverb.ShelfHiFreq.Manual['@Value'] = 1000;
effectReverb.DecayTime.Manual['@Value'] = projectData.effects.param1 * 8000; // max 8 seconds which is kinda close to how it sounds in KO effectReverb.ShelfLoFreq.Manual['@Value'] = 1000;
effectReverb.ShelfHiGain.Manual['@Value'] = projectData.effects.param2; // param2 is "color" which roughly matches the high shelf gain cut amount effectReverb.DecayTime.Manual['@Value'] = values.decayTime;
effectReverb.ShelfHighOn.Manual['@Value'] = values.highShelfOn ? 'true' : 'false';
effectReverb.ShelfHiGain.Manual['@Value'] = values.highShelfGain;
effectReverb.ShelfLowOn.Manual['@Value'] = values.lowShelfOn ? 'true' : 'false';
effectReverb.ShelfLoGain.Manual['@Value'] = values.lowShelfGain;
effectReverb.MixReflect.Manual['@Value'] = 1;
effectReverb.MixDiffuse.Manual['@Value'] = 1;
effectReverb.MixDirect.Manual['@Value'] = 0;
returnTrack.DeviceChain.DeviceChain.Devices = { Reverb: effectReverb }; returnTrack.DeviceChain.DeviceChain.Devices = { Reverb: effectReverb };
} }
@@ -993,15 +1001,16 @@ async function buildReturnTrack(
if (projectData.effects.effectType === EffectType.Delay) { if (projectData.effects.effectType === EffectType.Delay) {
const effectDelayTemplate = await loadTemplate<ALSDelay>('effectDelay', abletonVersion); const effectDelayTemplate = await loadTemplate<ALSDelay>('effectDelay', abletonVersion);
const effectDelay = structuredClone(effectDelayTemplate.Delay); const effectDelay = structuredClone(effectDelayTemplate.Delay);
const values = delayEffectValues(projectData.effects.param1, projectData.effects.param2);
effectDelay.DelayLine_SyncL.Manual['@Value'] = 'false'; effectDelay.DelayLine_SyncL.Manual['@Value'] = 'false';
effectDelay.DelayLine_SyncR.Manual['@Value'] = 'false'; effectDelay.DelayLine_SyncR.Manual['@Value'] = 'false';
// don't even ask how I came up with this formula // don't even ask how I came up with this formula
effectDelay.DelayLine_TimeL.Manual['@Value'] = effectDelay.DelayLine_TimeL.Manual['@Value'] = values.time;
0.017397 * (287.725 ** projectData.effects.param1 - 1); effectDelay.DelayLine_TimeR.Manual['@Value'] = values.time;
effectDelay.DelayLine_TimeR.Manual['@Value'] = effectDelay.Feedback.Manual['@Value'] = values.feedback;
0.017397 * (287.725 ** projectData.effects.param1 - 1); effectDelay.Filter_On.Manual['@Value'] = 'false';
effectDelay.Feedback.Manual['@Value'] = projectData.effects.param2; effectDelay.DryWet.Manual['@Value'] = 1;
returnTrack.DeviceChain.DeviceChain.Devices = { Delay: effectDelay }; returnTrack.DeviceChain.DeviceChain.Devices = { Delay: effectDelay };
} }
@@ -1012,9 +1021,11 @@ async function buildReturnTrack(
abletonVersion, abletonVersion,
); );
const effectDistortion = structuredClone(effectDistortionTemplate.Overdrive); const effectDistortion = structuredClone(effectDistortionTemplate.Overdrive);
const values = distortionEffectValues(projectData.effects.param1, projectData.effects.param2);
effectDistortion.Drive.Manual['@Value'] = projectData.effects.param1 * 50; effectDistortion.Drive.Manual['@Value'] = values.drive;
effectDistortion.Tone.Manual['@Value'] = projectData.effects.param2 * 100; effectDistortion.Tone.Manual['@Value'] = values.tone;
effectDistortion.DryWet.Manual['@Value'] = 100;
returnTrack.DeviceChain.DeviceChain.Devices = { Overdrive: effectDistortion }; returnTrack.DeviceChain.DeviceChain.Devices = { Overdrive: effectDistortion };
} }
@@ -1024,13 +1035,16 @@ async function buildReturnTrack(
const effectChorus = structuredClone( const effectChorus = structuredClone(
abletonVersion === '10' ? (effectChorusTemplate as any).Chorus : effectChorusTemplate.Chorus2, abletonVersion === '10' ? (effectChorusTemplate as any).Chorus : effectChorusTemplate.Chorus2,
); );
const values = chorusEffectValues(projectData.effects.param1, projectData.effects.param2);
if (abletonVersion === '10') { if (abletonVersion === '10') {
effectChorus.ModAmount.Manual['@Value'] = projectData.effects.param1 * 6.5; effectChorus.ModFreq.Manual['@Value'] = values.rate;
effectChorus.LfoMultiplier.Manual['@Value'] = 'false';
} else { } else {
effectChorus.Amount.Manual['@Value'] = projectData.effects.param1; effectChorus.Rate.Manual['@Value'] = values.rate;
} }
effectChorus.Feedback.Manual['@Value'] = projectData.effects.param2 * 0.8; effectChorus.Feedback.Manual['@Value'] = values.feedback;
effectChorus.DryWet.Manual['@Value'] = 1;
returnTrack.DeviceChain.DeviceChain.Devices = ( returnTrack.DeviceChain.DeviceChain.Devices = (
abletonVersion === '10' ? { Chorus: effectChorus } : { Chorus2: effectChorus } abletonVersion === '10' ? { Chorus: effectChorus } : { Chorus2: effectChorus }
@@ -1041,9 +1055,12 @@ async function buildReturnTrack(
if (projectData.effects.effectType === EffectType.Filter) { if (projectData.effects.effectType === EffectType.Filter) {
const effectFilterTemplate = await loadTemplate<ALSFilter>('effectFilter', abletonVersion); const effectFilterTemplate = await loadTemplate<ALSFilter>('effectFilter', abletonVersion);
const effectFilter = structuredClone(effectFilterTemplate.AutoFilter); const effectFilter = structuredClone(effectFilterTemplate.AutoFilter);
const values = filterEffectValues(projectData.effects.param1, projectData.effects.param2);
effectFilter.Cutoff.Manual['@Value'] = 20 + projectData.effects.param1 * (135 - 20); effectFilter.FilterType.Manual['@Value'] = values.filterType;
effectFilter.Resonance.Manual['@Value'] = projectData.effects.param2 * 1.25; effectFilter.LegacyFilterType.Manual['@Value'] = values.filterType;
effectFilter.Cutoff.Manual['@Value'] = values.cutoff;
effectFilter.Resonance.Manual['@Value'] = values.resonance;
returnTrack.DeviceChain.DeviceChain.Devices = { AutoFilter: effectFilter }; returnTrack.DeviceChain.DeviceChain.Devices = { AutoFilter: effectFilter };
} }
@@ -1054,8 +1071,19 @@ async function buildReturnTrack(
abletonVersion, abletonVersion,
); );
const effectCompressor = structuredClone(effectCompressorTemplate.Compressor2); const effectCompressor = structuredClone(effectCompressorTemplate.Compressor2);
const values = compressorEffectValues(projectData.effects.param1, projectData.effects.param2);
// params are ignored for now, just putting a basic compressor effectCompressor.Threshold.Manual['@Value'] = values.threshold;
effectCompressor.Ratio.Manual['@Value'] = values.ratio;
effectCompressor.Attack.Manual['@Value'] = values.attack;
effectCompressor.Release.Manual['@Value'] = values.release;
effectCompressor.AutoReleaseControlOnOff.Manual['@Value'] = 'false';
effectCompressor.Gain.Manual['@Value'] = 0;
effectCompressor.GainCompensation.Manual['@Value'] = 'true';
effectCompressor.DryWet.Manual['@Value'] = 1;
effectCompressor.Model.Manual['@Value'] = 0;
effectCompressor.Knee.Manual['@Value'] = 6;
effectCompressor.LookAhead.Manual['@Value'] = 0;
returnTrack.DeviceChain.DeviceChain.Devices = { Compressor2: effectCompressor }; returnTrack.DeviceChain.DeviceChain.Devices = { Compressor2: effectCompressor };
} }

View File

@@ -1,5 +1,3 @@
type AbletonVersion = '10' | '11';
function normalized(value: number) { function normalized(value: number) {
return Math.max(0, Math.min(1, value)); return Math.max(0, Math.min(1, value));
} }
@@ -32,7 +30,7 @@ export function distortionEffectValues(x: number, y: number) {
}; };
} }
export function chorusEffectValues(x: number, y: number, _abletonVersion: AbletonVersion) { export function chorusEffectValues(x: number, y: number) {
return { return {
rate: 0.1 * 100 ** normalized(x), rate: 0.1 * 100 ** normalized(x),
feedback: normalized(y) * 0.95, feedback: normalized(y) * 0.95,