diff --git a/tests/ableton-automation.test.ts b/tests/ableton-automation.test.ts index 22c2a86..3939f94 100644 --- a/tests/ableton-automation.test.ts +++ b/tests/ableton-automation.test.ts @@ -3,6 +3,7 @@ import test from 'node:test'; import { buildArrangementEnvelopes, buildClipEnvelopes, + convertFaderValue, getFaderValueAt, } from '../src/lib/exporters/ableton/automation'; import { AblGroupPatternOccurrence, AblTrack } from '../src/lib/transformers/ableton'; @@ -79,3 +80,37 @@ 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('uses each Simpler target duration and One-Shot release direction', () => { + assert.equal( + convertFaderValue(FaderParam.REL, 1, track, false, { + soundLength: 10, + maxValue: 2000, + invertValue: true, + }), + 0, + ); + assert.equal( + convertFaderValue(FaderParam.REL, 0, track, false, { + soundLength: 10, + maxValue: 2000, + invertValue: true, + }), + 2000, + ); + + const envelopes = buildClipEnvelopes( + occurrence, + { + [FaderParam.REL]: [ + { session: 23000, soundLength: 2 }, + { session: 23001, soundLength: 4, maxValue: 2000, invertValue: true }, + ], + }, + track, + false, + ); + + assert.equal(envelopes[0].Automation.Events.FloatEvent[2]['@Value'], 1500); + assert.equal(envelopes[1].Automation.Events.FloatEvent[2]['@Value'], 1000); +}); diff --git a/tests/ableton-effects.test.ts b/tests/ableton-effects.test.ts new file mode 100644 index 0000000..b361b7f --- /dev/null +++ b/tests/ableton-effects.test.ts @@ -0,0 +1,67 @@ +import assert from 'node:assert/strict'; +import test from 'node:test'; +import { + chorusEffectValues, + compressorEffectValues, + delayEffectValues, + distortionEffectValues, + filterEffectValues, + reverbEffectValues, +} from '../src/lib/exporters/ableton/effects'; + +test('maps delay length and feedback inside Live ranges', () => { + assert.deepEqual(delayEffectValues(0, 0), { time: 0.001, feedback: 0 }); + const maximum = delayEffectValues(1, 1); + assert.ok(maximum.time <= 5); + assert.equal(maximum.feedback, 0.95); +}); + +test('maps reverb color around a neutral center', () => { + assert.deepEqual(reverbEffectValues(0, 0), { + decayTime: 200, + highShelfOn: true, + highShelfGain: 0.19999999999999996, + lowShelfOn: false, + lowShelfGain: 1, + }); + assert.deepEqual(reverbEffectValues(1, 0.5), { + decayTime: 8000, + highShelfOn: false, + highShelfGain: 1, + lowShelfOn: false, + lowShelfGain: 1, + }); + assert.equal(reverbEffectValues(0.5, 1).lowShelfGain, 0.19999999999999996); +}); + +test('maps distortion and chorus controls to their documented roles', () => { + assert.deepEqual(distortionEffectValues(1, 1), { drive: 50, tone: 100 }); + assert.deepEqual(chorusEffectValues(0, 0), { rate: 0.1, feedback: 0 }); + assert.deepEqual(chorusEffectValues(1, 1), { rate: 10, feedback: 0.95 }); +}); + +test('maps the filter cutoff to low-pass and high-pass halves', () => { + assert.deepEqual(filterEffectValues(0, 0), { + filterType: 0, + cutoff: 20, + resonance: 0, + }); + assert.equal(filterEffectValues(0.5, 1).cutoff, 135); + assert.equal(filterEffectValues(0.5, 1).filterType, 0); + assert.equal(filterEffectValues(1, 1).cutoff, 135); + assert.equal(filterEffectValues(1, 1).filterType, 1); + assert.equal(filterEffectValues(1, 1).resonance, 1.25); +}); + +test('maps compressor drive and speed across a usable range', () => { + assert.deepEqual(compressorEffectValues(0, 0), { + threshold: 1, + ratio: 4, + attack: 50, + release: 500, + }); + const maximum = compressorEffectValues(1, 1); + assert.ok(Math.abs(maximum.threshold - 0.015848931924611134) < 1e-12); + assert.ok(Math.abs(maximum.attack - 0.1) < 1e-12); + assert.ok(Math.abs(maximum.release - 30) < 1e-12); +});