elektron-lfo 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,550 @@
1
+ # Elektron Digitakt II LFO - Comprehensive Test Plan
2
+
3
+ ## Test Overview
4
+
5
+ This document contains 18 specific test cases for the Digitakt II LFO system, focusing on edge cases and tricky behavioral scenarios. Each test includes the question, expected behavior based on the specification, and detailed test case procedures.
6
+
7
+ ---
8
+
9
+ ## Part 1: Test Questions with Expected Answers
10
+
11
+ | # | Question | Expected Answer | Difficulty |
12
+ |---|----------|-----------------|------------|
13
+ | 1 | What happens when SPD is -64 and the LFO plays backward? Does it advance the same number of phase steps as SPD=+64 forward? | Yes. Both SPD=64 and SPD=-64 produce the same cycle duration when multiplied by MULT. The difference is direction: positive plays forward (0→1→0), negative plays backward (0→-1→0). | High |
14
+ | 2 | In ONE mode with SPH=64 (middle of cycle) and SPD=-32, does the LFO stop after 1 full backward cycle from phase 0.5? | Yes. ONE mode requires exactly one complete cycle from start phase regardless of direction. With backward motion, it still travels the full cycle length before stopping. | High |
15
+ | 3 | If DEPTH=0, does the LFO still consume CPU/generate output internally even though modulation = 0? | Yes (likely). The LFO state machine continues running internally; only the final output is scaled to zero. This affects fade envelope timing and internal phase position. | Medium |
16
+ | 4 | When DEPTH is negative (e.g., -32), is the modulation simply inverted, or does it also invert the waveform itself? | Modulation is scaled by (depth/64). For negative depth, output = raw_lfo_value * (-32/64) = -0.5 * raw_value. The waveform shape stays the same; only polarity inverts. | Medium |
17
+ | 5 | In MODE=HLD (Hold), if a trigger arrives while FADE is non-zero, does the fade continue to progress in the background, or does it reset? | The fade should reset on trigger (based on handleTrigger code). HLD samples the current output at trigger time, but the underlying LFO continues running. Fade progress resets to 0. | High |
18
+ | 6 | For the RND (Random) waveform, if MULT is set very high (2048) creating audio-rate modulation, does the random step timing scale proportionally, or stay fixed at ~16 steps per cycle? | Timing scales proportionally. RND generates ~16 steps per cycle regardless of MULT. At MULT=2048, you get 16 random changes per audio-rate cycle, which translates to very rapid random modulation. | High |
19
+ | 7 | When MODE=HLF with SPH=0 and the LFO reaches phase 0.5, does it stop exactly at 0.5 or does it overshoot slightly due to discrete update intervals? | It should stop at exactly 0.5 (phase wrapping handles boundary cases). However, if update intervals are large, slight overshoot is possible. Hardware likely samples at fixed intervals. | Medium |
20
+ | 8 | If FADE=-64 (maximum fade IN) in ONE mode, does the modulation reach full depth before ONE completes its cycle, or is the fade slower than the cycle? | Fade rate is calculated as |FADE|/64 applied to cycle timing. At FADE=-64, it fades at maximum speed, likely completing fade-in before or during the ONE cycle. | Medium |
21
+ | 9 | Can LFO3 modulate LFO2's MULT, LFO2 modulate LFO1's MULT, and all three cross-modulate simultaneously without feedback loops breaking the system? | Yes, the hierarchy allows this: LFO3 can modulate LFO1 and LFO2; LFO2 can modulate LFO1. No circular dependencies exist. However, rapid changes could cause audio artifacts. | High |
22
+ | 10 | When SPD=1 and MULT=1 (slowest possible, ~4 minutes per cycle at 120 BPM), does the LFO continue updating smoothly, or does it become quantized/stepped due to low phase increment? | Updates are continuous (not quantized to phase steps). Phase increments by tiny amounts each update. Visual quantization might appear due to display resolution. | Low |
23
+ | 11 | If MODE=TRG with a very fast trigger rate and FADE=-30 (fade IN), can you trigger faster than the fade completes, stacking multiple fading envelopes? | Yes. Each trigger resets fade progress to 0. Multiple rapid triggers create overlapping fade envelopes. Without proper modulation destination mixing, only the most recent envelope is audible. | Medium |
24
+ | 12 | In FADE, is the fade duration absolute (e.g., "always 500ms to complete") or relative to the LFO cycle time? | Relative to cycle time. Fade rate = |FADE|/64 applied per update. Longer cycles = slower fade in absolute time; shorter cycles = faster fade. | High |
25
+ | 13 | When waveform is changed from TRI to RND while the LFO is mid-cycle, does it continue from the current phase position, or does it reset? | Should continue from current phase position. Changing waveform only affects the output calculation function, not the phase tracker. Output may jump abruptly. | Medium |
26
+ | 14 | For unipolar waveforms (EXP, RMP), does DEPTH=+32 produce a range of 0 to +0.5, or does it scale from the unipolar -1.0 to +1.0 range like bipolar waveforms? | Unipolar output (0 to +1) is scaled by depth/64. DEPTH=+32 produces 0 to +0.5. Unipolar waveforms never go negative regardless of depth sign (before scaling). | Medium |
27
+ | 15 | What happens when FADE switches from -30 (fade IN) to +30 (fade OUT) mid-cycle? Does the modulation fade out from its current level, or does it immediately jump back to full depth before fading out? | Should fade out from current level. Fade multiplier is already at some value; switching fade sign changes direction without resetting progress. Result: smoother envelope transition. | High |
28
+ | 16 | If MODE=HLF starting at SPH=100 going forward, is the "halfway point" at phase 0.5 from the start (0.5 + 0.78 = 1.28 wrapped = 0.28), or is it always globally at phase 0.5? | Halfway is relative to start phase: `(start_phase + 0.5) % 1.0`. From SPH=100 (0.78), halfway = 0.28. LFO stops at phase 0.28, not 0.5. | High |
29
+ | 17 | For MODE=ONE with SPD=-48 (backward), starting from SPH=0, does the LFO travel backward to reach the start phase again, or does it travel forward (interpreting "one cycle" as always going positive)? | Travels backward. ONE mode follows the direction set by SPD sign. Negative speed = backward travel. Still requires exactly one complete 360° cycle to complete. | High |
30
+ | 18 | When MODE=FRE and the LFO has been running continuously for hours, does phase wrapping at 1.0 ever cause accumulated floating-point rounding errors that gradually shift the LFO frequency? | Theoretically possible in 32-bit implementations. 64-bit double-precision should be stable indefinitely. Hardware likely uses fixed-point or integer phase math to avoid this. | Low |
31
+
32
+ ---
33
+
34
+ ## Part 2: Detailed Test Case Descriptions
35
+
36
+ ### TEST 1: Negative Speed Symmetry
37
+ **Question:** Does SPD=-64 produce the same cycle duration as SPD=+64?
38
+
39
+ **Setup:**
40
+ - Create two identical LFO configurations
41
+ - Config A: WAVE=SIN, SPD=+64, MULT=16, MODE=FRE, all others default
42
+ - Config B: WAVE=SIN, SPD=-64, MULT=16, MODE=FRE, all others default
43
+
44
+ **Test Procedure:**
45
+ 1. Start both LFOs simultaneously
46
+ 2. Measure time for Config A to complete 1 full cycle
47
+ 3. Measure time for Config B to complete 1 full cycle (backward)
48
+ 4. Record phase direction for each (Config A: 0→1→0, Config B: 0→-1→0)
49
+
50
+ **Expected Result:**
51
+ - Both complete in identical time (128 phase steps / (64 × 16) = 0.125 bars)
52
+ - Config A phase increases, Config B phase decreases
53
+ - Both produce same frequency of modulation
54
+
55
+ **Spec Reference:** Line 96-102 (SPD range and negative behavior)
56
+
57
+ ---
58
+
59
+ ### TEST 2: ONE Mode with Non-Zero Start Phase (Backward)
60
+ **Question:** Does ONE mode with negative speed respect the start phase boundary?
61
+
62
+ **Setup:**
63
+ - WAVE=SIN, SPD=-32, MULT=8, MODE=ONE, SPH=64, all others default
64
+
65
+ **Test Procedure:**
66
+ 1. Reset LFO to initial state
67
+ 2. Trigger note (starts phase at 0.5)
68
+ 3. Record phase position every 100ms until LFO stops
69
+ 4. Verify stopping condition: phase returns to exactly 0.5
70
+
71
+ **Expected Result:**
72
+ - LFO travels backward from 0.5 through 0.25, 0.0, wraps to 1.0, 0.75, 0.5
73
+ - Completes exactly 360° of backward travel
74
+ - Stops at phase 0.5 (same as start phase)
75
+ - isRunning flag becomes false
76
+
77
+ **Spec Reference:** Lines 443-461 (ONE mode stop detection for both directions)
78
+
79
+ ---
80
+
81
+ ### TEST 3: Depth = 0 Behavior
82
+ **Question:** Does a LFO with DEPTH=0 still update internal state?
83
+
84
+ **Setup:**
85
+ - WAVE=RND, SPD=32, MULT=4, MODE=TRG, DEPTH=0, FADE=-30
86
+
87
+ **Test Procedure:**
88
+ 1. Configure LFO as above
89
+ 2. Trigger at t=0ms
90
+ 3. Immediately change DEPTH from 0 to +32
91
+ 4. Observe output
92
+
93
+ **Expected Result:**
94
+ - With DEPTH=0, output=0 before step 3 (modulation calculation scales by 0/64)
95
+ - Internal LFO is still updating: phase advancing, fade progressing
96
+ - After changing DEPTH, modulation appears immediately (not delayed by previous fade)
97
+ - This proves internal state continued updating despite DEPTH=0
98
+
99
+ **Spec Reference:** Lines 189-194 (DEP calculation)
100
+
101
+ ---
102
+
103
+ ### TEST 4: Negative Depth with Unipolar Waveform
104
+ **Question:** Does negative depth on EXP waveform invert the unipolar (0 to +1) shape?
105
+
106
+ **Setup:**
107
+ - Config A: WAVE=EXP, SPD=16, MULT=8, DEPTH=+32, MODE=FRE
108
+ - Config B: WAVE=EXP, SPD=16, MULT=8, DEPTH=-32, MODE=FRE
109
+ - Sample output at phase 0.0, 0.5, 1.0
110
+
111
+ **Test Procedure:**
112
+ 1. Generate Config A output values at phases: 0.0, 0.25, 0.5, 0.75, 1.0
113
+ - EXP at these phases: ~0.0, ~0.18, ~0.47, ~0.81, ~1.0
114
+ - With DEPTH=+32: multiply each by 32/64 = 0.5
115
+ - Expected: 0.0, 0.09, 0.235, 0.405, 0.5
116
+ 2. Generate Config B same phases
117
+ - With DEPTH=-32: multiply each by -32/64 = -0.5
118
+ - Expected: 0.0, -0.09, -0.235, -0.405, -0.5
119
+ 3. Compare values
120
+
121
+ **Expected Result:**
122
+ - Negative depth inverts the output without changing waveform shape
123
+ - EXP waveform still accelerates upward; values are simply negative
124
+ - Result: modulation destination moves in opposite direction but with same curve character
125
+
126
+ **Spec Reference:** Lines 180-187 (DEP behavior)
127
+
128
+ ---
129
+
130
+ ### TEST 5: HLD Mode with Fade Reset on Trigger
131
+ **Question:** Does FADE progress reset when a trigger occurs in HLD mode?
132
+
133
+ **Setup:**
134
+ - WAVE=TRI, SPD=8, MULT=4, MODE=HLD, DEPTH=+48, FADE=-16
135
+ - Trigger spacing: 250ms apart
136
+
137
+ **Test Procedure:**
138
+ 1. Trigger at t=0ms
139
+ - Capture output at t=0, 50, 100, 150, 200ms
140
+ - Note fade progression (should fade in from 0 to some value)
141
+ 2. Trigger at t=250ms
142
+ - Immediately capture output (should sample current LFO value)
143
+ - Continue capturing at t=250, 300, 350, 400ms
144
+ - Note if fade resets to 0 or continues from previous value
145
+
146
+ **Expected Result:**
147
+ - First segment: fade progresses from 0 upward (fade IN starts at 0)
148
+ - Trigger at 250ms: fadeProgress resets to 0
149
+ - Second segment: fade starts ramping up again from 0
150
+ - HLD captures the held value, but fade envelope restarts
151
+
152
+ **Spec Reference:** Lines 545-561 (HLD trigger handler resets fadeProgress)
153
+
154
+ ---
155
+
156
+ ### TEST 6: RND Waveform at Audio-Rate Speed
157
+ **Question:** Does RND waveform generate 16 steps per cycle regardless of MULT?
158
+
159
+ **Setup:**
160
+ - WAVE=RND, SPD=32, MULT=512, MODE=FRE, DEPTH=+63
161
+ - MULT=512 creates: 32 × 512 = 16384 phase steps per bar = 128 cycles per bar
162
+
163
+ **Test Procedure:**
164
+ 1. Generate RND waveform output at 16384 sample positions across one bar
165
+ 2. Count number of distinct output values (steps) generated
166
+ 3. Calculate average "hold time" per step
167
+
168
+ **Expected Result:**
169
+ - Approximately 128 complete "16-step" cycles in one bar
170
+ - Each cycle contains ~16 distinct random values
171
+ - Average 128 phase steps per random value (16384 / 128 ≈ 128)
172
+ - RND does NOT accelerate to 128 steps per cycle; stays at 16 steps per cycle
173
+
174
+ **Spec Reference:** Lines 38, 361-373 (RND generates new value every 1/16th of phase, ~16 steps per cycle)
175
+
176
+ ---
177
+
178
+ ### TEST 7: HLF Mode Boundary Precision
179
+ **Question:** Does the LFO stop exactly at the halfway point or does it overshoot?
180
+
181
+ **Setup:**
182
+ - WAVE=SIN, SPD=16, MULT=4, MODE=HLF, SPH=0
183
+ - Polling interval: 1ms (precise sampling)
184
+
185
+ **Test Procedure:**
186
+ 1. Trigger at t=0 (phase = 0.0)
187
+ 2. Sample phase position every 1ms until isRunning becomes false
188
+ 3. Record final phase position with 4 decimal places
189
+ 4. Calculate "overshoot" if any: |final_phase - 0.5|
190
+
191
+ **Expected Result:**
192
+ - Final phase should be 0.5000 (±0.001 tolerance)
193
+ - Overshoot should be < 0.01 phase units
194
+ - isRunning becomes false in the same update where phase reaches 0.5
195
+
196
+ **Spec Reference:** Lines 464-487 (HLF mode stop detection with boundary crossing)
197
+
198
+ ---
199
+
200
+ ### TEST 8: Maximum FADE IN with ONE Mode Completion
201
+ **Question:** Does FADE=-64 complete fade-in before ONE mode stops?
202
+
203
+ **Setup:**
204
+ - WAVE=TRI, SPD=8, MULT=16, MODE=ONE, SPH=0, FADE=-64, DEPTH=+48
205
+ - Measure fade multiplier and phase position continuously
206
+
207
+ **Test Procedure:**
208
+ 1. Trigger at t=0
209
+ 2. Sample (phase, fadeMultiplier, output) every 10ms
210
+ 3. Record when isRunning becomes false
211
+ 4. Check fadeMultiplier value at that moment
212
+
213
+ **Expected Result:**
214
+ - fadeMultiplier should reach 1.0 (or very close) before ONE mode completes
215
+ - If FADE=-64 (maximum fade rate), fade should be nearly complete by cycle end
216
+ - Fade rate = 64/64 = 1.0 (fastest possible)
217
+ - ONE cycle duration at these settings = 1 bar = ~2000ms at 120 BPM
218
+ - Fade-in should complete in << 2000ms
219
+
220
+ **Spec Reference:** Lines 496-511 (Fade envelope timing)
221
+
222
+ ---
223
+
224
+ ### TEST 9: Three-LFO Cross-Modulation Chain
225
+ **Question:** Can LFO3→LFO2→LFO1 modulation work simultaneously without instability?
226
+
227
+ **Setup:**
228
+ - LFO1: WAVE=SIN, SPD=16, MULT=4, MODE=FRE
229
+ - LFO1.SPD is modulation destination for LFO2
230
+ - LFO2: WAVE=TRI, SPD=8, MULT=8, MODE=FRE
231
+ - LFO2.MULT is modulation destination for LFO3
232
+ - LFO3: WAVE=RMP, SPD=32, MULT=2, MODE=FRE
233
+
234
+ **Test Procedure:**
235
+ 1. Run all three LFOs simultaneously for 10 seconds
236
+ 2. Monitor LFO1.SPD: should oscillate due to LFO2 modulation
237
+ 3. Monitor LFO2.MULT: should appear to change due to LFO3 modulation
238
+ 4. Observe output waveforms for discontinuities or artifacts
239
+
240
+ **Expected Result:**
241
+ - LFO3 modulates LFO2.MULT smoothly
242
+ - LFO2 (with modulated MULT) modulates LFO1.SPD
243
+ - LFO1 (with modulated SPD) produces output with dynamic modulation rate
244
+ - No feedback loops (since hierarchy only goes LFO3→LFO2→LFO1)
245
+ - Outputs remain continuous without jumps or discontinuities
246
+
247
+ **Spec Reference:** Lines 10-14 (LFO hierarchy), Lines 276-277 (modulation destinations)
248
+
249
+ ---
250
+
251
+ ### TEST 10: Slowest LFO Smooth Continuity
252
+ **Question:** At SPD=1, MULT=1 (~4-minute cycle), is phase smooth or stepped?
253
+
254
+ **Setup:**
255
+ - WAVE=SIN, SPD=1, MULT=1, MODE=FRE, DEPTH=+32
256
+ - Polling at 60 FPS (16.67ms intervals)
257
+
258
+ **Test Procedure:**
259
+ 1. Run LFO for at least 30 seconds
260
+ 2. Capture phase position and output value every frame
261
+ 3. Calculate phase delta between consecutive frames
262
+ 4. Verify phase delta is not quantized to discrete steps
263
+
264
+ **Expected Result:**
265
+ - Phase delta per frame: ~16.67ms ÷ 256000ms (cycle time) ≈ 0.000065 phase units
266
+ - Phase values should be smoothly increasing, not jumping to discrete steps
267
+ - Output may appear stepped on screen due to display resolution, but internal phase is continuous
268
+
269
+ **Spec Reference:** Lines 418-441 (phase update calculations use floating-point, not quantized steps)
270
+
271
+ ---
272
+
273
+ ### TEST 11: Overlapping Fade Envelopes with Rapid Triggers
274
+ **Question:** Can you trigger faster than a fade completes?
275
+
276
+ **Setup:**
277
+ - WAVE=EXP, SPD=32, MULT=4, MODE=TRG, DEPTH=+48, FADE=-30
278
+ - Trigger rhythm: every 100ms (faster than expected fade completion)
279
+
280
+ **Test Procedure:**
281
+ 1. Trigger at t=0, 100, 200, 300ms
282
+ 2. Capture output at:
283
+ - t=0: just after trigger 1
284
+ - t=50: before trigger 2 (fade mid-progress)
285
+ - t=100: trigger 2 happens
286
+ - t=150: fade resets and progresses again
287
+ - t=200: trigger 3
288
+ - Continue pattern
289
+ 3. Verify each trigger resets fade progress
290
+
291
+ **Expected Result:**
292
+ - Each trigger resets fadeProgress to 0
293
+ - Output rises from 0 toward full depth, then resets on next trigger
294
+ - Creates a "staggered" ramp pattern, not smooth crescendo
295
+ - Each trigger is independent; no accumulated envelope
296
+
297
+ **Spec Reference:** Lines 545-572 (handleTrigger resets fadeProgress for TRG and ONE modes)
298
+
299
+ ---
300
+
301
+ ### TEST 12: Fade Timing Relative to Cycle, Not Absolute
302
+ **Question:** Is fade duration relative to LFO cycle, or fixed in milliseconds?
303
+
304
+ **Setup:**
305
+ - Config A: WAVE=TRI, SPD=16, MULT=8, FADE=-32, DEPTH=+48 (1-bar cycle)
306
+ - Config B: WAVE=TRI, SPD=32, MULT=16, FADE=-32, DEPTH=+48 (1/2-bar cycle)
307
+ - Both at 120 BPM
308
+
309
+ **Test Procedure:**
310
+ 1. Trigger Config A at t=0
311
+ 2. Measure time until fadeMultiplier reaches 0.95
312
+ - Expected: ~50% of cycle time (fade rate = 32/64)
313
+ - Config A cycle: 1 bar = 2000ms
314
+ - Expected fade-to-0.95: ~1000ms
315
+ 3. Repeat for Config B
316
+ - Cycle: 1/2 bar = 1000ms
317
+ - Expected fade-to-0.95: ~500ms
318
+ 4. Compare fade completion times
319
+
320
+ **Expected Result:**
321
+ - Fade rate is 32/64 = 0.5 (half-speed)
322
+ - Each LFO reaches 95% fade in ~50% of its own cycle time
323
+ - Config A takes ~1000ms, Config B takes ~500ms
324
+ - Proves fade timing is RELATIVE to cycle, not absolute
325
+
326
+ **Spec Reference:** Lines 496-511 (fadeRate calculation uses cyclesPerMs, making fade relative to cycle time)
327
+
328
+ ---
329
+
330
+ ### TEST 13: Waveform Switch Mid-Cycle
331
+ **Question:** Changing waveform mid-cycle: continuous phase or reset?
332
+
333
+ **Setup:**
334
+ - WAVE=TRI, SPD=16, MULT=8, MODE=FRE
335
+ - Run for 500ms, then change WAVE to RND
336
+ - Monitor phase continuity
337
+
338
+ **Test Procedure:**
339
+ 1. Start LFO at t=0 with WAVE=TRI
340
+ 2. Capture phase and output every 50ms
341
+ 3. At t=500ms, change WAVE to RND
342
+ 4. Continue capturing every 50ms for 500ms more
343
+ 5. Observe discontinuities in output values
344
+
345
+ **Expected Result:**
346
+ - Phase position continues smoothly across waveform change
347
+ - At t=500ms, if phase=0.2, output changes from TRI(0.2) to RND(0.2)
348
+ - RND may produce wildly different output value due to random sampling
349
+ - But phase itself doesn't reset; it continues from 0.2
350
+ - Output can jump abruptly, but no "pop" or discontinuity in phase
351
+
352
+ **Spec Reference:** Lines 325-373 (Waveform functions are stateless; phase is the state)
353
+
354
+ ---
355
+
356
+ ### TEST 14: Unipolar Waveform Depth Scaling
357
+ **Question:** Does unipolar EXP with DEPTH=+32 produce 0 to 0.5 or something else?
358
+
359
+ **Setup:**
360
+ - Config A: WAVE=EXP, SPD=16, MULT=8, DEPTH=+32, MODE=FRE
361
+ - Config B: WAVE=EXP, SPD=16, MULT=8, DEPTH=+64, MODE=FRE
362
+ - Sample output at phase 0.0, 0.5, 1.0
363
+
364
+ **Test Procedure:**
365
+ 1. Generate EXP waveform raw values:
366
+ - Phase 0.0: ~0.0
367
+ - Phase 0.5: ~0.47 (middle of exponential)
368
+ - Phase 1.0: ~1.0
369
+ 2. For Config A, multiply by 32/64 = 0.5:
370
+ - Expected: 0.0, 0.235, 0.5
371
+ 3. For Config B, multiply by 64/64 = 1.0:
372
+ - Expected: 0.0, 0.47, 1.0
373
+
374
+ **Expected Result:**
375
+ - EXP (unipolar 0 to 1) × (32/64) = 0 to 0.5
376
+ - EXP (unipolar 0 to 1) × (64/64) = 0 to 1.0
377
+ - Unipolar waveforms ALWAYS 0 to +1 before depth scaling
378
+ - Negative depth only applies to bipolar waveforms in a meaningful way (negating bipolar range)
379
+ - Negative depth on unipolar: scales downward from 0, producing 0 to -0.5 etc.
380
+
381
+ **Spec Reference:** Lines 36-37, 189-194 (Waveform polarity, DEP calculation)
382
+
383
+ ---
384
+
385
+ ### TEST 15: Fade Direction Switch Mid-Envelope
386
+ **Question:** Switching FADE from -30 to +30 mid-cycle: fade out from current, or jump?
387
+
388
+ **Setup:**
389
+ - WAVE=SIN, SPD=8, MULT=8, MODE=TRG, DEPTH=+48, FADE=-30
390
+ - Trigger at t=0
391
+ - At t=500ms, change FADE to +30
392
+
393
+ **Test Procedure:**
394
+ 1. Trigger at t=0 (fades in from 0 toward 1.0)
395
+ 2. Capture output at t=0, 100, 200, 300, 400, 500ms
396
+ 3. At t=500ms, switch FADE from -30 to +30
397
+ 4. Continue capturing at t=500, 600, 700, 800, 900ms
398
+ 5. Note fadeMultiplier values
399
+
400
+ **Expected Result:**
401
+ - t=0-500ms: fadeMultiplier increases from 0 toward 1.0 (fade IN)
402
+ - At t=500ms, fadeMultiplier ≈ 0.5 (halfway faded in)
403
+ - t=500ms: FADE changes to +30
404
+ - fadeMultiplier does NOT jump; stays at 0.5
405
+ - Fade direction reverses (positive FADE = fade OUT)
406
+ - t=500-1000ms: fadeMultiplier decreases from 0.5 toward 0 (fade OUT)
407
+ - Result: smooth envelope transition, no jump
408
+
409
+ **Spec Reference:** Lines 504-510 (Fade direction is determined by FADE sign; fadeMultiplier continues from current value)
410
+
411
+ ---
412
+
413
+ ### TEST 16: HLF Mode with Non-Zero Start Phase
414
+ **Question:** Is halfway relative to start phase, or always at global 0.5?
415
+
416
+ **Setup:**
417
+ - WAVE=SIN, SPD=16, MULT=8, MODE=HLF, SPH=100 (≈0.78 normalized), DEPTH=+48
418
+
419
+ **Test Procedure:**
420
+ 1. Trigger at t=0 (phase starts at 0.78)
421
+ 2. Calculate expected stop point: (0.78 + 0.5) % 1.0 = 0.28
422
+ 3. Run LFO until isRunning becomes false
423
+ 4. Record final phase position
424
+
425
+ **Expected Result:**
426
+ - LFO travels forward from 0.78 → 0.9 → 1.0 (wraps) → 0.0 → 0.15 → 0.28
427
+ - Stops at phase 0.28 (not at global 0.5)
428
+ - Confirms halfway calculation is `(start_phase + 0.5) % 1.0`
429
+
430
+ **Spec Reference:** Lines 464-487 (HLF mode calculation: `halfwayPhase = (startPhase + 0.5) % 1.0`)
431
+
432
+ ---
433
+
434
+ ### TEST 17: ONE Mode Backward with Zero Start Phase
435
+ **Question:** Does backward ONE mode travel backward through a full cycle?
436
+
437
+ **Setup:**
438
+ - WAVE=TRI, SPD=-48, MULT=4, MODE=ONE, SPH=0, DEPTH=+48
439
+
440
+ **Test Procedure:**
441
+ 1. Trigger at t=0 (phase = 0.0)
442
+ 2. Capture phase every 50ms until isRunning becomes false
443
+ 3. Verify direction (should be negative/decreasing)
444
+
445
+ **Expected Result:**
446
+ - Phase travels backward: 0.0 → -0.1 (wraps to 0.9) → 0.8 → ... → 0.1 → 0.0
447
+ - Complete 360° backward rotation
448
+ - Stops at phase 0.0 (start phase)
449
+ - isRunning becomes false after full cycle
450
+ - Output is inverted TRI shape due to backward motion
451
+
452
+ **Spec Reference:** Lines 443-461 (ONE mode detects backward completion: `newPhase <= startPhase && prevPhase > startPhase`)
453
+
454
+ ---
455
+
456
+ ### TEST 18: Long-Running FRE Mode Floating-Point Stability
457
+ **Question:** Does a continuously running FRE LFO drift frequency over hours due to rounding errors?
458
+
459
+ **Setup:**
460
+ - WAVE=SIN, SPD=16, MULT=8, MODE=FRE, DEPTH=+32
461
+ - Run for simulated 24 hours at 1000 FPS (simulated time)
462
+
463
+ **Test Procedure:**
464
+ 1. Calculate theoretical cycle time: 1 bar = 2000ms
465
+ 2. Measure actual cycles in:
466
+ - First hour (simulated)
467
+ - 12th hour
468
+ - 24th hour
469
+ 3. Compare cycle counts to expected (should be identical)
470
+
471
+ **Expected Result:**
472
+ - 32-bit floating point: may accumulate small errors after many hours
473
+ - 64-bit double precision: should be stable indefinitely
474
+ - Hardware likely uses integer/fixed-point arithmetic: no drift
475
+
476
+ **Spec Reference:** Lines 429-441 (Phase wrapping uses floating-point math)
477
+
478
+ ---
479
+
480
+ ## Part 3: Discrepancy Analysis
481
+
482
+ ### Identified Specification Ambiguities
483
+
484
+ 1. **Random Waveform Step Timing at Audio Rates**
485
+ - Spec states RND changes ~16x more frequently than other waveforms
486
+ - Unclear if this is a fixed number of steps per cycle or a time-based rate
487
+ - Recommendation: TEST 6 will clarify
488
+
489
+ 2. **HLF Mode Backward Behavior**
490
+ - Spec describes forward behavior clearly but backward is implied
491
+ - Expected behavior: halfway point reverses direction (subtract 0.5 instead of add)
492
+ - Recommendation: Verify with hardware
493
+
494
+ 3. **Fade Timing During Stopped ONE/HLF**
495
+ - Spec shows code where fade continues after ONE stops
496
+ - Question: should fade be frozen once ONE reaches its endpoint?
497
+ - Current spec: fade continues (line 509 shows fadeMultiplier is maintained)
498
+
499
+ 4. **HLD Mode and Modulation Destinations**
500
+ - HLD samples output at trigger time and holds it
501
+ - Spec doesn't clarify if held value updates when modulation destination changes
502
+ - Expected: held value is frozen; modulation to underlying LFO doesn't affect held output
503
+
504
+ 5. **DEPTH Asymmetry (-64 to +63)**
505
+ - Spec mentions SPD, DEP, FADE all have this asymmetry
506
+ - No explanation of why hardware chose this design
507
+ - May affect certain calculations; recommend testing edge cases at -64 vs +63
508
+
509
+ ### Recommendations
510
+
511
+ 1. **Validate RND behavior** at MULT=2048 (TEST 6)
512
+ 2. **Test HLF backward** with SPH != 0 (TEST 16)
513
+ 3. **Verify FADE behavior** when ONE mode completes (TEST 8)
514
+ 4. **Test HLD and cross-modulation** to confirm held value independence
515
+ 5. **Test -64 edge cases** for DEP and FADE parameters
516
+
517
+ ---
518
+
519
+ ## Test Execution Checklist
520
+
521
+ Use this checklist to track test completion:
522
+
523
+ - [ ] TEST 1: Negative Speed Symmetry
524
+ - [ ] TEST 2: ONE Mode with Non-Zero Start Phase (Backward)
525
+ - [ ] TEST 3: Depth = 0 Behavior
526
+ - [ ] TEST 4: Negative Depth with Unipolar Waveform
527
+ - [ ] TEST 5: HLD Mode with Fade Reset on Trigger
528
+ - [ ] TEST 6: RND Waveform at Audio-Rate Speed
529
+ - [ ] TEST 7: HLF Mode Boundary Precision
530
+ - [ ] TEST 8: Maximum FADE IN with ONE Mode Completion
531
+ - [ ] TEST 9: Three-LFO Cross-Modulation Chain
532
+ - [ ] TEST 10: Slowest LFO Smooth Continuity
533
+ - [ ] TEST 11: Overlapping Fade Envelopes with Rapid Triggers
534
+ - [ ] TEST 12: Fade Timing Relative to Cycle
535
+ - [ ] TEST 13: Waveform Switch Mid-Cycle
536
+ - [ ] TEST 14: Unipolar Waveform Depth Scaling
537
+ - [ ] TEST 15: Fade Direction Switch Mid-Envelope
538
+ - [ ] TEST 16: HLF Mode with Non-Zero Start Phase
539
+ - [ ] TEST 17: ONE Mode Backward with Zero Start Phase
540
+ - [ ] TEST 18: Long-Running FRE Mode Floating-Point Stability
541
+
542
+ ---
543
+
544
+ ## Document Metadata
545
+
546
+ - **Created:** Based on DIGITAKT_II_LFO_SPEC.md and DIGITAKT_II_LFO_PRESETS.md
547
+ - **Test Cases:** 18 comprehensive edge-case scenarios
548
+ - **Coverage:** LFO parameters (WAVE, SPD, MULT, SPH, MODE, DEP, FADE)
549
+ - **Focus Areas:** Negative values, unipolar waveforms, cross-modulation, boundary conditions, fade behavior, mode interactions
550
+ - **Status:** Ready for implementation and execution
package/package.json ADDED
@@ -0,0 +1,26 @@
1
+ {
2
+ "name": "elektron-lfo",
3
+ "version": "1.0.0",
4
+ "description": "Elektron LFO engine simulator implementation with CLI visualization",
5
+ "main": "dist/index.js",
6
+ "module": "src/index.ts",
7
+ "types": "dist/index.d.ts",
8
+ "bin": {
9
+ "elektron-lfo": "./src/cli/index.ts"
10
+ },
11
+ "scripts": {
12
+ "dev": "bun run src/cli/index.ts",
13
+ "test": "bun test",
14
+ "test:watch": "bun test --watch",
15
+ "build": "bun build src/index.ts --outdir dist --target node",
16
+ "cli": "bun run src/cli/index.ts",
17
+ "typecheck": "tsc --noEmit"
18
+ },
19
+ "devDependencies": {
20
+ "@types/bun": "latest",
21
+ "typescript": "^5.0.0"
22
+ },
23
+ "engines": {
24
+ "bun": ">=1.0.0"
25
+ }
26
+ }