seq_clk

Sequencer clock controller, analog style
Author: Are Leistad
License: BSD
Github: drj/seq/seq_clk.axo

Inlets

frac32.bipolar Clock frequency mod

frac32.bipolar Gate length mod

bool32 Enable playing

bool32 Play backwards

bool32 Play steps counting up and down

bool32 Repeat end steps for upndown mode

bool32.rising Reset to step 0 on next clock

bool32.rising Reset to step 0 immediately

Outlets

int32 Step number out

bool32 Gate/clock out

bool32.pulse Step clock output

bool32.pulse Pulse when step 1 starts

Parameters

frac32.u.map.ratio Gate length

int32 seqlength

frac32.s.map.lfopitch Clock frequency modulation

Declaration
uint32_t start_sequence;
uint32_t first_run;
uint32_t phase;
uint32_t old_phase;
uint32_t old_reset_sync;
uint32_t old_reset_imm;
uint32_t reset_sync;
uint32_t reset_imm;
uint32_t supress_trig; // Avoid new gate on current step for stop -> run
uint32_t enforce_trig; // Ensure that we get a retrigger on reset immediate if
                       // pressed when gate was on
int32_t step_count;
int32_t old_step_count;
int32_t step_direction;
Init
start_sequence = 0;
first_run = 1;
phase = 0;
old_phase = 0;
old_reset_sync = 0;
old_reset_imm = 0;
reset_sync = 0;
reset_imm = 0;
supress_trig = 0;
enforce_trig = 0;
step_count = 0;
old_step_count = 0;
step_direction = 1;
Control Rate
int32_t freq;
int32_t gatelength;

// Reset output pulses
outlet_clock = 0;
outlet_start = 0;

if (start_sequence < 2) {
  if (inlet_reverse) {
    step_count = param_seqlength - 1;
    step_direction = -1;
  }
  start_sequence++;
}

// Catch counter reset requests, with immediate reset taking priority

if (inlet_rstimm && !old_reset_imm) {
  reset_imm = 1;
} else if (inlet_rstsync && !old_reset_sync) {
  reset_sync = 1;
}
old_reset_sync = inlet_rstsync;
old_reset_imm = inlet_rstimm;

if (inlet_run) {
  MTOFEXTENDED(param_tempo + inlet_tempo, freq);
  phase += freq >> 2;

  if (first_run) {
    // Need to force a start pulse the very first run, since no count will
    // trigger before the seconf step
    outlet_start = 1;
    first_run = 0;
  } else if (phase < old_phase) {
    // Clock!

    if (inlet_upndown) {
      step_count += step_direction;
      if (step_count < 0) {
        step_direction = 1;
        if (inlet_endrep) {
          step_count = 0;
        } else {
          step_count = 1;
        }
      } else if (step_count >= param_seqlength) {
        step_direction = -1;
        if (inlet_endrep) {
          step_count = param_seqlength - 1;
        } else {
          step_count = param_seqlength - 2;
        }
      }
    } else {
      step_direction = 1;
      if (inlet_reverse) {
        step_count--;
      } else {
        step_count++;
      }

      if (step_count < 0) {
        step_count = param_seqlength - 1;
      } else if (step_count >= param_seqlength) {
        step_count = 0;
      }
    }

    if (step_count == 0) {
      outlet_start = 1;
    }

    outlet_clock = 1;
  }

  // Handle sequencer reset, either immediate or synchronized to next step

  if (reset_imm) {
    phase = 0;
    reset_imm = 0;
    reset_sync = 0;
    enforce_trig = 1;
    if (inlet_reverse) {
      step_count = param_seqlength - 1;
      step_direction = -1;
    } else {
      step_count = 0;
      step_direction = 1;
    }
  } else if (reset_sync) {
    if (phase < old_phase) {
      phase = 0;
      reset_imm = 0;
      reset_sync = 0;
      if (inlet_reverse) {
        step_count = param_seqlength - 1;
        step_direction = -1;
      } else {
        step_count = 0;
        step_direction = 1;
      }
    }
  }

  // The gate length is variable from 0% to 100%

  gatelength = param_gatelength + inlet_gatelength;
  if (gatelength > (1 << 27)) {
    gatelength = (1 << 27);
  } else if (gatelength < 0) {
    gatelength = 0;
  }

  // Handle the gate correctly for all cases
  //   Supressed trigger : avoids new gate on current step for stop -> run
  //   Enforced trigger  : force a new gate on reset immediate if pressed when
  //   the gate was on Normal trigger    : running gate pulse with variable gate
  //   length

  if (supress_trig) {
    outlet_gate = 0;
    if (step_count != old_step_count) {
      supress_trig = 0;
    }
  } else if (enforce_trig) {
    outlet_gate = 0;
    enforce_trig = 0;
  } else {
    if ((phase >> 5) > gatelength) {
      outlet_gate = 0;
    } else {
      outlet_gate = 1;
    }
  }

  outlet_step = step_count;
  old_step_count = step_count;
  old_phase = phase;
} else {
  outlet_gate = 0;
  outlet_step = step_count;
  supress_trig = 1; // Avoid retriggering current step on stop -> run
}

Privacy

© 2024 Zrna Research