Improve Ableton automation export for filters and add dedicated filter building function

This commit is contained in:
2026-07-13 05:29:26 +02:00
parent aecf040b87
commit eb5a25fe0a
2 changed files with 46 additions and 22 deletions

View File

@@ -47,9 +47,9 @@ export function convertFaderValue(
case FaderParam.PTC:
return (value - 0.5) * 10;
case FaderParam.LPF:
return filterFreqFromNormalized(value * 0.9);
return 20 + value * 0.9 * 115;
case FaderParam.HPF:
return filterFreqFromNormalized(value * 0.7);
return 20 + value * 0.7 * 115;
case FaderParam.FX:
return value;
case FaderParam.ATK:

View File

@@ -122,6 +122,25 @@ function findSimplers(node: any, result: any[] = []): any[] {
return result;
}
function findAutoFilters(node: any, result: any[] = []): any[] {
if (Array.isArray(node)) {
node.forEach((item) => {
findAutoFilters(item, result);
});
return result;
}
if (!node || typeof node !== 'object') {
return result;
}
if (node.AutoFilter) {
result.push(node.AutoFilter);
}
Object.values(node).forEach((value) => {
findAutoFilters(value, result);
});
return result;
}
function addTarget(
targets: AbletonAutomationTargets,
parameter: FaderParam,
@@ -157,13 +176,17 @@ function trackAutomationTargets(midiTrack: any, includeArrangementMixer: boolean
findSimplers(midiTrack.DeviceChain.DeviceChain.Devices).forEach((simpler) => {
addTarget(targets, FaderParam.PTC, targetFor(simpler.Pitch.TransposeKey));
addTarget(targets, FaderParam.TUNE, targetFor(simpler.Pitch.TransposeFine));
addTarget(targets, FaderParam.LPF, targetFor(simpler.Filter.Slot.Value.SimplerFilter.Freq));
addTarget(targets, FaderParam.HPF, targetFor(simpler.Filter.Slot.Value.SimplerFilter.Freq));
addTarget(targets, FaderParam.ATK, targetFor(simpler.VolumeAndPan.Envelope.AttackTime));
addTarget(targets, FaderParam.REL, targetFor(simpler.VolumeAndPan.Envelope.ReleaseTime));
addTarget(targets, FaderParam.MOD, targetFor(simpler.Pitch.PitchLfoAmount));
});
findAutoFilters(midiTrack.DeviceChain.DeviceChain.Devices).forEach((filter) => {
const parameter =
Number(filter.FilterType.Manual['@Value']) === 1 ? FaderParam.HPF : FaderParam.LPF;
addTarget(targets, parameter, targetFor(filter.Cutoff));
});
return targets;
}
@@ -395,24 +418,6 @@ async function buildSimplerDevice(koTrack: AblTrack, abletonVersion: AbletonVers
device.Lfo.Slot.Value.SimplerLfo.RateType.Manual['@Value'] = 1; // tempo synced 1/16
}
if (koTrack.faderParams[FaderParam.LPF] !== -1 && koTrack.faderParams[FaderParam.LPF] !== 1) {
device.Filter.IsOn.Manual['@Value'] = 'true';
device.Filter.Slot.Value.SimplerFilter.Type.Manual['@Value'] = 0; // Low Pass
device.Filter.Slot.Value.SimplerFilter.Slope.Manual['@Value'] = 'true'; // 24db/oct
device.Filter.Slot.Value.SimplerFilter.Freq.Manual['@Value'] = filterFreqFromNormalized(
koTrack.faderParams[FaderParam.LPF] * 0.9, // lowering max freq to match KO behavior
);
}
if (koTrack.faderParams[FaderParam.HPF] !== -1 && koTrack.faderParams[FaderParam.HPF] !== 0) {
device.Filter.IsOn.Manual['@Value'] = 'true';
device.Filter.Slot.Value.SimplerFilter.Type.Manual['@Value'] = 1; // High Pass
device.Filter.Slot.Value.SimplerFilter.Slope.Manual['@Value'] = 'false'; // 12db/oct
device.Filter.Slot.Value.SimplerFilter.Freq.Manual['@Value'] = filterFreqFromNormalized(
koTrack.faderParams[FaderParam.HPF] * 0.7, // lowering max freq to match KO behavior
);
}
// TODO: add bandpass filter
if (koTrack.timeStretch === 'bars') {
@@ -471,6 +476,25 @@ async function buildSimplerDevice(koTrack: AblTrack, abletonVersion: AbletonVers
};
}
async function buildFaderFilterDevice(
parameter: FaderParam.LPF | FaderParam.HPF,
value: number,
abletonVersion: AbletonVersion,
) {
const filterTemplate = await loadTemplate<ALSFilter>('effectFilter', abletonVersion);
const filter = structuredClone(filterTemplate.AutoFilter);
const highPass = parameter === FaderParam.HPF;
filter.LastPresetRef.Value = {};
filter.UserName['@Value'] = highPass ? 'EP HPF' : 'EP LPF';
filter.FilterType.Manual['@Value'] = highPass ? 1 : 0;
filter.LegacyFilterType.Manual['@Value'] = highPass ? 1 : 0;
filter.Slope.Manual['@Value'] = highPass ? 'false' : 'true';
filter.Cutoff.Manual['@Value'] = highPass
? 20 + value * 0.7 * 115
: 20 + value * 0.9 * 115;
return { AutoFilter: filter };
}
async function buildDrumRackDevice(koTrack: AblTrack, abletonVersion: AbletonVersion) {
const [drumRackTemplate, drumBranchTemplate] = await Promise.all([
loadTemplate<ALSDrumRack>('drumRack', abletonVersion),