elektron-lfo 1.0.1 → 1.0.3

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.
package/dist/index.js CHANGED
@@ -283,7 +283,15 @@ function checkModeStop(config, state, previousPhase, currentPhase) {
283
283
  const isForward = config.speed >= 0;
284
284
  if (config.mode === "ONE") {
285
285
  if (state.cycleCount >= 1) {
286
- return { shouldStop: true, cycleCompleted: true };
286
+ if (isForward) {
287
+ if (startPhase === 0 || currentPhase >= startPhase) {
288
+ return { shouldStop: true, cycleCompleted: true };
289
+ }
290
+ } else {
291
+ if (startPhase === 0 || currentPhase <= startPhase) {
292
+ return { shouldStop: true, cycleCompleted: true };
293
+ }
294
+ }
287
295
  }
288
296
  } else if (config.mode === "HLF") {
289
297
  const halfPhase = (startPhase + 0.5) % 1;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "elektron-lfo",
3
- "version": "1.0.1",
3
+ "version": "1.0.3",
4
4
  "description": "Elektron LFO engine simulator implementation with CLI visualization",
5
5
  "main": "dist/index.js",
6
6
  "module": "src/index.ts",
@@ -112,11 +112,23 @@ export function checkModeStop(
112
112
  const isForward = config.speed >= 0;
113
113
 
114
114
  if (config.mode === 'ONE') {
115
- // ONE mode: Stop after completing one full cycle
116
- // We detect this by checking if cycleCount has incremented (phase wrapped)
117
- // and we've returned to or passed the start phase
115
+ // ONE mode: Stop after completing one full cycle back to startPhase
116
+ // For non-zero startPhase, we need to:
117
+ // 1. Wait for phase to wrap (cycleCount >= 1)
118
+ // 2. Then continue until we reach/pass the startPhase again
118
119
  if (state.cycleCount >= 1) {
119
- return { shouldStop: true, cycleCompleted: true };
120
+ if (isForward) {
121
+ // Forward: stop when current phase reaches or passes startPhase after wrapping
122
+ // Handle the case where startPhase is 0 (stop immediately on wrap)
123
+ if (startPhase === 0 || currentPhase >= startPhase) {
124
+ return { shouldStop: true, cycleCompleted: true };
125
+ }
126
+ } else {
127
+ // Backward: stop when current phase reaches or goes below startPhase after wrapping
128
+ if (startPhase === 0 || currentPhase <= startPhase) {
129
+ return { shouldStop: true, cycleCompleted: true };
130
+ }
131
+ }
120
132
  }
121
133
  } else if (config.mode === 'HLF') {
122
134
  // HLF mode: Stop after half cycle (0.5 phase distance from start)
@@ -240,6 +240,64 @@ describe('checkModeStop - ONE mode', () => {
240
240
  const result = checkModeStop(config, state, 0.05, 0.95);
241
241
  expect(result.shouldStop).toBe(true);
242
242
  });
243
+
244
+ test('with non-zero startPhase, does NOT stop immediately after wrap - must reach startPhase', () => {
245
+ // Bug fix: With startPhase 53 (~0.414), the LFO should:
246
+ // 1. Start at 0.414
247
+ // 2. Run to 1.0, wrap to 0.0 (cycleCount = 1)
248
+ // 3. Continue from 0.0 to 0.414 (startPhase)
249
+ // 4. THEN stop
250
+ const config = createConfig({ mode: 'ONE', speed: 16, startPhase: 53 });
251
+ const startPhaseNormalized = 53 / 128; // ~0.414
252
+
253
+ // After wrap, phase is 0.1, which is BEFORE startPhase (0.414)
254
+ // Should NOT stop yet
255
+ const stateAfterWrap = createState({
256
+ hasTriggered: true,
257
+ startPhaseNormalized,
258
+ cycleCount: 1,
259
+ });
260
+ const resultBeforeStart = checkModeStop(config, stateAfterWrap, 0.05, 0.1);
261
+ expect(resultBeforeStart.shouldStop).toBe(false);
262
+
263
+ // Phase reaches 0.3, still before startPhase - should NOT stop
264
+ const resultStillBefore = checkModeStop(config, stateAfterWrap, 0.2, 0.3);
265
+ expect(resultStillBefore.shouldStop).toBe(false);
266
+
267
+ // Phase crosses startPhase (0.414) - NOW should stop
268
+ const resultAtStart = checkModeStop(config, stateAfterWrap, 0.4, 0.45);
269
+ expect(resultAtStart.shouldStop).toBe(true);
270
+ expect(resultAtStart.cycleCompleted).toBe(true);
271
+ });
272
+
273
+ test('with non-zero startPhase (backward), continues after wrap until reaching startPhase', () => {
274
+ // Backward direction with startPhase 53 (~0.414):
275
+ // 1. Start at 0.414
276
+ // 2. Run backward to 0.0, wrap to 1.0 (cycleCount = 1)
277
+ // 3. Continue from 1.0 down to 0.414
278
+ // 4. THEN stop
279
+ const config = createConfig({ mode: 'ONE', speed: -16, startPhase: 53 });
280
+ const startPhaseNormalized = 53 / 128; // ~0.414
281
+
282
+ // After wrap going backward, phase is 0.9, which is ABOVE startPhase (0.414)
283
+ // Should NOT stop yet
284
+ const stateAfterWrap = createState({
285
+ hasTriggered: true,
286
+ startPhaseNormalized,
287
+ cycleCount: 1,
288
+ });
289
+ const resultAfterWrap = checkModeStop(config, stateAfterWrap, 0.95, 0.9);
290
+ expect(resultAfterWrap.shouldStop).toBe(false);
291
+
292
+ // Phase reaches 0.5, still above startPhase - should NOT stop
293
+ const resultStillAbove = checkModeStop(config, stateAfterWrap, 0.6, 0.5);
294
+ expect(resultStillAbove.shouldStop).toBe(false);
295
+
296
+ // Phase crosses startPhase (0.414) going down - NOW should stop
297
+ const resultAtStart = checkModeStop(config, stateAfterWrap, 0.45, 0.4);
298
+ expect(resultAtStart.shouldStop).toBe(true);
299
+ expect(resultAtStart.cycleCompleted).toBe(true);
300
+ });
243
301
  });
244
302
 
245
303
  describe('checkModeStop - HLF mode', () => {