Sequencer firing multiple events/ or one step behind

B/c there’s a slightly time loss during the time done is set to true and we check it ourselves, and only then we re-invoke start(), I don’t think class Countdown fits fully the requirements of your “sequencers”. :sweat:

Just made a variant of it called Interval, where done is set to true over & over at a fixed period time.

We now need to call Interval::intervalDone() in order to check whether the previous interval had been completed.

As a bonus, in place or in addition to check Interval::intervalDone(), we can also assign our own customized Interval.Callback::run() to the field Interval::callback. :smile_cat:

Well, better see it for yourself. Here’s the code for both the “.pde” example and the “.java” library itself: :star_struck:

“IntervalClass.pde”:

/** 
 * Interval Class (v1.0)
 * GoToLoop (2018/Jul/09)
 *
 * https://Discourse.Processing.org/t/
 * sequencer-firing-multiple-events-or-one-step-behind/1571/12
 */

import gotoloop.time.Interval;

static final float SECS = 1;
static final int WAIT_TIME = (int) (SECS * 1000);

final Interval interval = new Interval(WAIT_TIME);
color bg;

void setup() {
  size(300, 180);
  smooth(3);
  frameRate(60);

  colorMode(RGB);
  fill(#FFFF00);

  textSize(0100);
  textAlign(CENTER, CENTER);

  final int m = millis(), t = m + WAIT_TIME;
  interval.start();
  println(m, t, t - m, TAB, interval);

  createCallback();
}

void draw() {
  if (interval.intervalDone())  bg = randomColor();
  background(bg);

  final String txt = millis() + "\n" + frameCount;
  text(txt, width>>1, height>>1);
}

color randomColor() {
  return (color) random(PImage.ALPHA_MASK);
}

void createCallback() {
  interval.callback = new Interval.Callback() {
    @Override public void run() {
      fill(randomColor());
    }
  };
}

“Interval.java”:

/** 
 * Interval Class (v1.0)
 * GoToLoop (2018/Jul/09)
 *
 * https://Discourse.Processing.org/t/
 * sequencer-firing-multiple-events-or-one-step-behind/1571/12
 */

package gotoloop.time;

import java.util.Timer;
import java.util.TimerTask;
import java.util.concurrent.atomic.AtomicBoolean;

public class Interval {
  protected static final Timer t = new Timer("Interval");
  protected final AtomicBoolean done = new AtomicBoolean(true);

  public Callback callback;
  public TimerTask task;

  public int period;
  public boolean precise = true;

  public Interval() {
  }

  public Interval(final int waitTime) { // milliseconds
    this(waitTime, null);
  }

  public Interval(final int waitTime, final Callback listener) {
    period = Math.abs(waitTime);
    callback = listener;
  }

  @Override public String toString() {
    return "Period: " + period;
  }

  public Interval start() {
    stop().done.set(false);
    task = new Timeout();

    if (precise)  t.scheduleAtFixedRate(task, period, period);
    else          t.schedule(task, period, period);

    return this;
  }

  public Interval stop() {
    if (task != null)  task.cancel();
    return this;
  }

  public boolean intervalDone() {
    return done.compareAndSet(true, false);
  }

  @FunctionalInterface public interface Callback {
    void run();
  }

  protected class Timeout extends TimerTask {
    @Override public void run() {
      done.set(true);
      if (callback != null)  callback.run();
    }
  }
}