Update Ableton MIDI exporter to support locked envelopes and improve robustness

This commit is contained in:
2026-07-14 06:51:10 +02:00
parent 06efaaa0ea
commit d5f86370d7
2 changed files with 15 additions and 5 deletions

View File

@@ -1,6 +1,9 @@
import type { AblMidiEvent } from '../../transformers/ableton'; import type { AblMidiEvent } from '../../transformers/ableton';
export type AbletonMidiControllerTargets = Record<string, { '@Id': number }>; export type AbletonMidiControllerTargets = Record<
string,
{ '@Id': number; LockEnvelope?: { '@Value': number | string } }
>;
type AutomationPoint = { type AutomationPoint = {
time: number; time: number;
@@ -36,10 +39,13 @@ export function buildMidiClipEnvelopes(
events: AblMidiEvent[], events: AblMidiEvent[],
controllerTargets: AbletonMidiControllerTargets, controllerTargets: AbletonMidiControllerTargets,
) { ) {
const target = controllerTargets[`ControllerTargets.${CHANNEL_PRESSURE_TARGET}`]?.['@Id']; const target = controllerTargets[`ControllerTargets.${CHANNEL_PRESSURE_TARGET}`];
if (target === undefined) { if (!target) {
return []; return [];
} }
if (target.LockEnvelope) {
target.LockEnvelope['@Value'] = 1;
}
const points: AutomationPoint[] = [{ time: SENTINEL_TIME, value: 0 }]; const points: AutomationPoint[] = [{ time: SENTINEL_TIME, value: 0 }];
let previousValue = 0; let previousValue = 0;
@@ -70,7 +76,7 @@ export function buildMidiClipEnvelopes(
return [ return [
{ {
'@Id': 0, '@Id': 0,
EnvelopeTarget: { PointeeId: { '@Value': target } }, EnvelopeTarget: { PointeeId: { '@Value': target['@Id'] } },
Automation: { Automation: {
Events: { FloatEvent: floatEvents(points) }, Events: { FloatEvent: floatEvents(points) },
AutomationTransformViewState: { AutomationTransformViewState: {

View File

@@ -83,14 +83,18 @@ test('decodes only the captured EP Channel Pressure record shape', () => {
}); });
test('builds native Live 10 Channel Pressure steps with raw values', () => { test('builds native Live 10 Channel Pressure steps with raw values', () => {
const controllerTargets = {
'ControllerTargets.129': { '@Id': 45123, LockEnvelope: { '@Value': 0 } },
};
const envelopes = buildMidiClipEnvelopes( const envelopes = buildMidiClipEnvelopes(
[midiEvent(192, 2, 20), midiEvent(96, 2, 75), midiEvent(96, 2, 20)], [midiEvent(192, 2, 20), midiEvent(96, 2, 75), midiEvent(96, 2, 20)],
{ 'ControllerTargets.129': { '@Id': 45123 } }, controllerTargets,
); );
const envelope = envelopes[0]; const envelope = envelopes[0];
const events = envelope.Automation.Events.FloatEvent; const events = envelope.Automation.Events.FloatEvent;
assert.equal(envelope.EnvelopeTarget.PointeeId['@Value'], 45123); assert.equal(envelope.EnvelopeTarget.PointeeId['@Value'], 45123);
assert.equal(controllerTargets['ControllerTargets.129'].LockEnvelope['@Value'], 1);
assert.deepEqual( assert.deepEqual(
events.map((event: any) => [event['@Time'], event['@Value']]), events.map((event: any) => [event['@Time'], event['@Value']]),
[ [