diff --git a/tests/ableton-automation.test.ts b/tests/ableton-automation.test.ts index c16b539..0a35c0e 100644 --- a/tests/ableton-automation.test.ts +++ b/tests/ableton-automation.test.ts @@ -86,3 +86,38 @@ test('uses absolute automation targets for Session clip envelopes', () => { 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), []); +}); diff --git a/tests/ableton-fader-policies.test.ts b/tests/ableton-fader-policies.test.ts new file mode 100644 index 0000000..b48f0e7 --- /dev/null +++ b/tests/ableton-fader-policies.test.ts @@ -0,0 +1,35 @@ +import assert from 'node:assert/strict'; +import test from 'node:test'; +import { + FADER_POLICIES, + convertGroupFaderValue, + getFaderPolicy, +} from '../src/lib/exporters/ableton/faderPolicies'; +import { AblTrack } from '../src/lib/transformers/ableton'; +import { FaderParam } from '../src/types/types'; + +const track = { volume: 0.4 } as AblTrack; + +test('defines an explicit policy for every EP group fader parameter', () => { + assert.equal(Object.keys(FADER_POLICIES).length, 12); + for (let parameter = FaderParam.LVL; parameter <= FaderParam.MOD; parameter++) { + assert.ok(getFaderPolicy(parameter)); + } +}); + +test('separates Live targets, note transforms and unsupported group parameters', () => { + assert.equal(getFaderPolicy(FaderParam.PAN).renderStrategy, 'target'); + assert.equal(getFaderPolicy(FaderParam.VEL).renderStrategy, 'note'); + assert.equal(getFaderPolicy(FaderParam.TIM).renderStrategy, 'unsupported'); + assert.equal(getFaderPolicy(FaderParam.ATK).renderStrategy, 'unsupported'); + assert.equal(getFaderPolicy(FaderParam.REL).renderStrategy, 'unsupported'); +}); + +test('uses one group-fader conversion path for mixer and pad targets', () => { + assert.equal(convertGroupFaderValue(FaderParam.LVL, 0.5, { track }), 0.2); + assert.equal(convertGroupFaderValue(FaderParam.PAN, 0.75, { track }), 0.5); + assert.equal( + convertGroupFaderValue(FaderParam.PTC, 0.75, { track, valueOffset: 7 }), + 9.5, + ); +}); diff --git a/tests/ableton-pad-sound.test.ts b/tests/ableton-pad-sound.test.ts new file mode 100644 index 0000000..f6cb519 --- /dev/null +++ b/tests/ableton-pad-sound.test.ts @@ -0,0 +1,47 @@ +import assert from 'node:assert/strict'; +import test from 'node:test'; +import { + getPadEnvelopeState, + getPadSoundState, +} from '../src/lib/exporters/ableton/padSound'; +import { AblTrack } from '../src/lib/transformers/ableton'; + +const track = { + volume: 0.75, + pitch: -3, + playMode: 'key', + pan: 0.25, + trimLeft: 100, + trimRight: 1000, + attack: 64, + release: 128, + timeStretch: 'bars', + timeStretchBpm: 92, + timeStretchBars: 2, + rootNote: 48, + soundLength: 4, +} as AblTrack; + +test('keeps static pad sound settings separate from group fader state', () => { + assert.deepEqual(getPadSoundState(track), { + amp: 0.75, + pitch: -3, + playMode: 'key', + pan: 0.25, + trimStart: 100, + trimEnd: 1000, + envelope: { attack: 64, release: 128 }, + time: { mode: 'bars', bpm: 92, bars: 2 }, + rootNote: 48, + soundLength: 4, + }); +}); + +test('maps only pad ENV values into the static Classic envelope', () => { + const envelope = getPadEnvelopeState(getPadSoundState(track)); + + assert.equal(envelope.classicAttackMs, (64 / 255) * 4000); + assert.equal(envelope.classicReleaseMs, (128 / 255) * 4000); + assert.equal(envelope.oneShotFadeInMs, 0); + assert.equal(envelope.oneShotFadeOutMs, 5); +});