diff --git a/src/lib/exporters/ableton/builders.ts b/src/lib/exporters/ableton/builders.ts index 9a7df93..06298bc 100644 --- a/src/lib/exporters/ableton/builders.ts +++ b/src/lib/exporters/ableton/builders.ts @@ -702,6 +702,7 @@ async function buildTrack( async function buildGroupTrack( koTrack: AblTrack, + groupLane: AblGroupLane | undefined, exporterParams: ExporterParams, maxScenes: number, ) { @@ -714,6 +715,21 @@ async function buildGroupTrack( groupTrack.Name.UserName['@Value'] = koTrack.group.toLocaleUpperCase(); setColorValue(groupTrack, 5 + _localGroupTrackColor++); groupTrack.Slots.GroupTrackSlot = []; + groupTrack.DeviceChain.Mixer.Sends = {}; + + if (!exporterParams.clips) { + if (koTrack.faderParams[FaderParam.LVL] !== -1) { + groupTrack.DeviceChain.Mixer.Volume.Manual['@Value'] = + koTrack.faderParams[FaderParam.LVL]; + } + if (koTrack.faderParams[FaderParam.PAN] !== -1) { + groupTrack.DeviceChain.Mixer.Pan.Manual['@Value'] = + (koTrack.faderParams[FaderParam.PAN] - 0.5) * 2; + } + } else { + groupTrack.DeviceChain.Mixer.Volume.Manual['@Value'] = 1; + groupTrack.DeviceChain.Mixer.Pan.Manual['@Value'] = 0; + } // adding empty slots for clips (or Ableton will crash) // must be at least 8 slots to avoid Ableton crashes @@ -726,18 +742,36 @@ async function buildGroupTrack( }); } - if (exporterParams.sendEffects && koTrack.faderParams[FaderParam.FX] !== -1) { + if ( + exporterParams.sendEffects && + !exporterParams.clips && + koTrack.faderParams[FaderParam.FX] !== -1 + ) { const trackSendHolderTemplate = await loadTemplate( 'trackSendHolder', abletonVersion, ); const trackSendHolder = structuredClone(trackSendHolderTemplate.TrackSendHolder); + trackSendHolder['@Id'] = 0; trackSendHolder.Send.Manual['@Value'] = koTrack.faderParams[FaderParam.FX]; groupTrack.DeviceChain.Mixer.Sends.TrackSendHolder = trackSendHolder; } + fixIds(groupTrack); + if (!exporterParams.clips && groupLane) { + const envelopes = buildArrangementEnvelopes( + groupLane.patterns, + groupAutomationTargets(groupTrack), + koTrack, + true, + ); + groupTrack.AutomationEnvelopes.Envelopes = envelopes.length + ? ({ AutomationEnvelope: envelopes } as any) + : {}; + } + return { GroupTrack: groupTrack }; } @@ -769,7 +803,12 @@ async function buildScenes( return result; } -async function buildTracks(tracks: AblTrack[], maxScenes: number, exporterParams: ExporterParams) { +async function buildTracks( + tracks: AblTrack[], + groupLanes: AblGroupLane[], + maxScenes: number, + exporterParams: ExporterParams, +) { const result = []; let trackGroupId = -1; let currentGroup = ''; @@ -777,14 +816,21 @@ async function buildTracks(tracks: AblTrack[], maxScenes: number, exporterParams // if the tracks are grouped, we need to create a group track for each group BEFORE the children midi tracks // this took me around 3 hours to figure out for (const koTrack of tracks.sort((a, b) => a.group.localeCompare(b.group))) { + const groupLane = groupLanes.find((lane) => lane.group === koTrack.group); if (exporterParams.groupTracks && koTrack.group !== currentGroup[0]) { currentGroup = koTrack.group; - const groupTrack = await buildGroupTrack(koTrack, exporterParams, maxScenes); + const groupTrack = await buildGroupTrack(koTrack, groupLane, exporterParams, maxScenes); trackGroupId = groupTrack.GroupTrack['@Id']; result.push(groupTrack); } - const midiTrack = await buildTrack(koTrack, maxScenes, trackGroupId, exporterParams); + const midiTrack = await buildTrack( + koTrack, + groupLane, + maxScenes, + trackGroupId, + exporterParams, + ); result.push(midiTrack); }