import assert from 'node:assert/strict'; import test from 'node:test'; import { buildArrangementEnvelopes, buildClipEnvelopes, convertFaderValue, getFaderValueAt, } from '../src/lib/exporters/ableton/automation'; import { AblGroupPatternOccurrence, AblTrack } from '../src/lib/transformers/ableton'; import { FaderParam } from '../src/types/types'; const faderParams = { [FaderParam.LVL]: 1, [FaderParam.PTC]: 0.5, [FaderParam.TIM]: 0.5, [FaderParam.LPF]: 1, [FaderParam.HPF]: 0, [FaderParam.FX]: 0, [FaderParam.ATK]: 0, [FaderParam.REL]: 0.25, [FaderParam.PAN]: 0.5, [FaderParam.TUNE]: 0.5, [FaderParam.VEL]: -1, [FaderParam.MOD]: 0, }; const occurrence: AblGroupPatternOccurrence = { group: 'a', patternNumber: 1, bars: 1, duration: 8, loopDuration: 4, sceneIndex: 0, sceneName: '01', offset: 0, timeSignature: { numerator: 4, denominator: 4 }, faderParams, faderAutomation: [ { parameter: FaderParam.PAN, position: 96, value: 0.75, flags: 0 }, { parameter: FaderParam.VEL, position: 192, value: 0.5, flags: 0 }, ], }; const track = { volume: 0.5, soundLength: 2, } as AblTrack; test('uses original note velocity until a VEL point is reached', () => { assert.equal(getFaderValueAt(occurrence, FaderParam.VEL, 96), undefined); assert.equal(getFaderValueAt(occurrence, FaderParam.VEL, 192), 0.5); }); test('preserves each pad pitch offset while applying group PTC automation', () => { assert.equal(convertFaderValue(FaderParam.PTC, 0.75, track, false, { valueOffset: 7 }), 9.5); }); test('tiles arrangement automation across the scene length', () => { const envelopes = buildArrangementEnvelopes( [occurrence], { [FaderParam.PAN]: [{ arrangement: 22000 }] }, track, false, ); const events = envelopes[0].Automation.Events.FloatEvent; assert.equal(envelopes[0].EnvelopeTarget.PointeeId['@Value'], 22000); assert.equal(events[0]['@Time'], -63072000); assert.deepEqual( events .filter((event: any) => event['@Time'] === 1 || event['@Time'] === 5) .map((event: any) => event['@Time']), [1, 5], ); }); test('uses absolute automation targets for Session clip envelopes', () => { const envelopes = buildClipEnvelopes( occurrence, { [FaderParam.PAN]: [{ session: 23000 }] }, track, false, ); assert.equal(envelopes[0].EnvelopeTarget.PointeeId['@Value'], 23000); assert.equal(envelopes[0].Automation.Events.FloatEvent[2]['@Time'], 1); assert.deepEqual(envelopes[0].LoopSlot, { Value: {} }); }); test('does not create Session envelopes without recorded parameter events', () => { const withoutPan = { ...occurrence, faderAutomation: occurrence.faderAutomation.filter( (point) => point.parameter !== FaderParam.PAN, ), }; const envelopes = buildClipEnvelopes( withoutPan, { [FaderParam.PAN]: [{ session: 23000 }] }, track, false, ); assert.deepEqual(envelopes, []); }); test('keeps research-only group parameters out of Live target envelopes', () => { const researchOccurrence = { ...occurrence, faderAutomation: [ { parameter: FaderParam.TIM, position: 0, value: 0.25, flags: 0 }, { parameter: FaderParam.ATK, position: 0, value: 0.5, flags: 0 }, { parameter: FaderParam.REL, position: 0, value: 0.75, flags: 0 }, ], }; const targets = { [FaderParam.TIM]: [{ session: 23000 }], [FaderParam.ATK]: [{ session: 23001 }], [FaderParam.REL]: [{ session: 23002 }], }; assert.deepEqual(buildClipEnvelopes(researchOccurrence, targets, track, false), []); });