import type { AblMidiEvent } from '../../transformers/ableton'; export type AbletonMidiControllerTargets = Record; type AutomationPoint = { time: number; value: number; }; const CHANNEL_PRESSURE_TARGET = 129; const SENTINEL_TIME = -63072000; export function decodeChannelPressure(event: AblMidiEvent) { if ( event.data.length !== 4 || event.data[0] !== 0xd0 || event.data[1] > 127 || event.data[2] !== 0xff || event.data[3] !== 0 ) { return undefined; } return event.data[1]; } function floatEvents(points: AutomationPoint[]) { return points.map((point, index) => ({ '@Id': index, '@Time': point.time, '@Value': point.value, })); } export function buildMidiClipEnvelopes( events: AblMidiEvent[], controllerTargets: AbletonMidiControllerTargets, ) { const target = controllerTargets[`ControllerTargets.${CHANNEL_PRESSURE_TARGET}`]?.['@Id']; if (target === undefined) { return []; } const points: AutomationPoint[] = [{ time: SENTINEL_TIME, value: 0 }]; let previousValue = 0; let eventCount = 0; events.forEach((event) => { const value = decodeChannelPressure(event); if (value === undefined) { return; } const time = event.position / 96; points.push({ time, value: previousValue }); points.push({ time, value }); previousValue = value; eventCount++; }); if (eventCount === 0) { return []; } return [ { '@Id': 0, EnvelopeTarget: { PointeeId: { '@Value': target } }, Automation: { Events: { FloatEvent: floatEvents(points) }, AutomationTransformViewState: { IsTransformPending: { '@Value': 'false' }, TimeAndValueTransforms: {}, }, }, LoopSlot: { Value: {} }, ScrollerTimePreserver: { LeftTime: { '@Value': 0 }, RightTime: { '@Value': 0 }, }, }, ]; }