diff --git a/src/lib/exporters/ableton/builders.ts b/src/lib/exporters/ableton/builders.ts index 48decd4..41d3e3c 100644 --- a/src/lib/exporters/ableton/builders.ts +++ b/src/lib/exporters/ableton/builders.ts @@ -800,6 +800,7 @@ async function buildTrack( if (!hasClip && hasSupportedAutomation) { sessionClips.push({ notes: [], + midiEvents: [], bars: pattern.bars, offset: pattern.offset, sceneBars: pattern.sceneBars, diff --git a/src/lib/parsers.ts b/src/lib/parsers.ts index 5d8129c..ea5fd6d 100644 --- a/src/lib/parsers.ts +++ b/src/lib/parsers.ts @@ -314,11 +314,18 @@ function getPatternsForScene(scenesIntermediate: IntermediateScenes, scenePatter if (lookupSceneData?.groups[group]) { const groupData = lookupSceneData.groups[group]; - Object.entries(groupData.notes).forEach(([padNum, notes]) => { + const sources = new Set(Object.keys(groupData.notes).map(Number)); + groupData.events.forEach((event) => { + if (event.type === 'midi' && event.source >= 0 && event.source < 12) { + sources.add(event.source); + } + }); + + [...sources].toSorted((a, b) => a - b).forEach((padNum) => { patterns.push({ pad: `${group}${padNum}` as PadCode, group, - notes, + notes: groupData.notes[padNum] || [], bars: groupData.bars, }); }); diff --git a/src/lib/transformers/ableton.ts b/src/lib/transformers/ableton.ts index 2b77b4a..0182d51 100644 --- a/src/lib/transformers/ableton.ts +++ b/src/lib/transformers/ableton.ts @@ -4,6 +4,7 @@ import { ExporterParams, FaderAutomationEvent, FaderParam, + MidiPatternEvent, Note, Pad, PadCode, @@ -44,6 +45,7 @@ export type AblNote = Note; export type AblClip = { notes: AblNote[]; + midiEvents: AblMidiEvent[]; bars: number; offset: number; sceneBars: number; @@ -54,6 +56,11 @@ export type AblClip = { groupPattern?: AblGroupPatternOccurrence; }; +export type AblMidiEvent = Pick< + MidiPatternEvent, + 'position' | 'source' | 'flags' | 'data' | 'rawData' +>; + export type AblFaderAutomationPoint = Pick & { parameter: FaderParam; }; @@ -166,6 +173,10 @@ function abletonTransformer(data: ProjectRawData, exporterParams: ExporterParams } const faderParams = data.settings.groupFaderParams[pad.group]; + const source = Number(pattern.pad.slice(1)); + const midiEvents = (scene.patternEvents[pattern.group]?.events || []).filter( + (event): event is MidiPatternEvent => event.type === 'midi' && event.source === source, + ); if (!track) { const soundId = findSoundIdByPad(pattern.pad, pads) || 0; @@ -204,6 +215,7 @@ function abletonTransformer(data: ProjectRawData, exporterParams: ExporterParams lane.clips.push({ offset, notes: pattern.notes, + midiEvents, bars: pattern.bars, sceneBars, sceneIndex, @@ -324,6 +336,7 @@ function abletonTransformer(data: ProjectRawData, exporterParams: ExporterParams if (!newClips[clip.sceneName]) { newClips[clip.sceneName] = structuredClone(clip); newClips[clip.sceneName].notes = []; + newClips[clip.sceneName].midiEvents = []; } newClips[clip.sceneName].notes = [ @@ -333,6 +346,10 @@ function abletonTransformer(data: ProjectRawData, exporterParams: ExporterParams note: drumPad, })), ]; + newClips[clip.sceneName].midiEvents = [ + ...newClips[clip.sceneName].midiEvents, + ...clip.midiEvents, + ]; }); });