// mod_step_lfo.omfx — OmniScript Modulator
// Stepped LFO modulator with configurable number of steps.
// Uses a for loop to quantize the LFO phase into N equal steps.

@name: Step LFO
@desc: Quantized stepped LFO modulator with variable step count
@mode: modulator

@param[0] name="Rate"   min=0.1  max=20.0 default=2.0  unit="Hz"
@param[1] name="Steps"  min=2    max=16   default=8
@param[2] name="Shape"  min=0    max=2    default=0
@param[3] name="Smooth" min=0.0  max=1.0  default=0.1

// Shape: 0=Saw, 1=Triangle, 2=Random-per-step

fn saw_wave(p) {
    return p * 2.0 - 1.0;
}

fn tri_wave(p) {
    if p < 0.5 {
        return p * 4.0 - 1.0;
    }
    return 3.0 - p * 4.0;
}

fn quantize_phase(p, n_steps) {
    // Snap phase to nearest step
    step_size = 1.0 / n_steps;
    return floor(p / step_size) * step_size;
}

@init
  phase = 0.0;
  prev_step = 0.0;
  rand_val = 0.0;
  smooth_out = 0.0;

@block
  n_steps = floor(param[1] + 0.5);

@sample
  // Advance phase
  phase = phase + param[0] / sample_rate;
  if phase >= 1.0 { phase = phase - 1.0; }

  // Quantize phase to step grid
  q_phase = quantize_phase(phase, n_steps);

  // Detect step change for random mode
  if abs(q_phase - prev_step) > 0.001 {
    rand_val = rand() * 2.0 - 1.0;
    prev_step = q_phase;
  }

  // Generate waveform at quantized phase
  shape = floor(param[2] + 0.5);
  if shape < 0.5 {
    raw = saw_wave(q_phase);
  }
  if shape >= 0.5 && shape < 1.5 {
    raw = tri_wave(q_phase);
  }
  if shape >= 1.5 {
    raw = rand_val;
  }

  // Smoothing (one-pole low-pass on output)
  sm = param[3] * 0.999 + 0.001;
  coeff = 1.0 - exp(-1.0 / (sm * 0.05 * sample_rate));
  smooth_out = smooth_out + coeff * (raw - smooth_out);

  output = smooth_out;
