diff --git a/src/lib/exporters/ableton/builders.ts b/src/lib/exporters/ableton/builders.ts index fd49bc1..e7b169b 100644 --- a/src/lib/exporters/ableton/builders.ts +++ b/src/lib/exporters/ableton/builders.ts @@ -7,7 +7,12 @@ import { ProjectSettings, } from '../../../types/types'; import { EFFECTS } from '../../constants'; -import abletonTransformer, { AblClip, AblScene, AblTrack } from '../../transformers/ableton'; +import abletonTransformer, { + AblClip, + AblGroupLane, + AblScene, + AblTrack, +} from '../../transformers/ableton'; import { getQuarterNotesPerBar } from '../utils'; import { ALSDrumBranch } from './types/drumBranch'; import { ALSDrumRack } from './types/drumRack'; @@ -25,6 +30,12 @@ import { ALSReturnTrack } from './types/returnTrack'; import { ALSScene, ALSSceneContent } from './types/scene'; import { ALSSimpler } from './types/simpler'; import { ALSTrackSendHolder } from './types/trackSendHolder'; +import { + AbletonAutomationTargets, + buildArrangementEnvelopes, + buildClipEnvelopes, + getFaderValueAt, +} from './automation'; import { filterFreqFromNormalized, fixIds, @@ -82,6 +93,94 @@ function setLive10SampleReference(fileRef: any, sampleName: string) { fileRef.LivePackId = { '@Value': '' }; } +function targetFor(parameter: any): { arrangement?: number; session?: number } { + return { + arrangement: + parameter?.AutomationTarget?.['@Id'] === undefined + ? undefined + : Number(parameter.AutomationTarget['@Id']), + session: + parameter?.ModulationTarget?.['@Id'] === undefined + ? undefined + : Number(parameter.ModulationTarget['@Id']), + }; +} + +function findSimplers(node: any, result: any[] = []): any[] { + if (Array.isArray(node)) { + node.forEach((item) => findSimplers(item, result)); + return result; + } + if (!node || typeof node !== 'object') { + return result; + } + if (node.OriginalSimpler) { + result.push(node.OriginalSimpler); + } + Object.values(node).forEach((value) => findSimplers(value, result)); + return result; +} + +function addTarget( + targets: AbletonAutomationTargets, + parameter: FaderParam, + target: { arrangement?: number; session?: number }, +) { + if (target.arrangement === undefined && target.session === undefined) { + return; + } + targets[parameter] = [...(targets[parameter] || []), target]; +} + +function trackAutomationTargets(midiTrack: any, includeArrangementMixer: boolean) { + const targets: AbletonAutomationTargets = {}; + const mixer = midiTrack.DeviceChain.Mixer; + const sequencer = midiTrack.DeviceChain.MainSequencer; + addTarget(targets, FaderParam.LVL, { + arrangement: includeArrangementMixer + ? Number(mixer.Volume.AutomationTarget['@Id']) + : undefined, + session: Number(sequencer.VolumeModulationTarget['@Id']), + }); + addTarget(targets, FaderParam.PAN, { + arrangement: includeArrangementMixer ? Number(mixer.Pan.AutomationTarget['@Id']) : undefined, + session: Number(mixer.Pan.ModulationTarget['@Id']), + }); + + const send = mixer.Sends?.TrackSendHolder?.Send; + if (send) { + const target = targetFor(send); + if (!includeArrangementMixer) { + target.arrangement = undefined; + } + addTarget(targets, FaderParam.FX, target); + } + + 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)); + }); + + return targets; +} + +function groupAutomationTargets(groupTrack: any) { + const targets: AbletonAutomationTargets = {}; + const mixer = groupTrack.DeviceChain.Mixer; + addTarget(targets, FaderParam.LVL, targetFor(mixer.Volume)); + addTarget(targets, FaderParam.PAN, targetFor(mixer.Pan)); + const send = mixer.Sends?.TrackSendHolder?.Send; + if (send) { + addTarget(targets, FaderParam.FX, targetFor(send)); + } + return targets; +} + async function buildMidiClip( koClip: AblClip, clipIdx: number,