Sequencer firing multiple events/ or one step behind

I have some code for a 16 step sequencer but I’m having problems with triggering the events.
I’ll post 2 versions of the code. In the first version the problem is that the event is triggert multiple times but it should only print once to the console:

int savedTime;
int totalTime = 250;

// An array of buttons
Button[] buttons = new Button[80];

int mc = 0;
int rx = -1;
int mx= 0;

void setup() {
  size(1200, 400);
  savedTime = millis();
  // A loop to evenly space out the buttons along the window
  for (int j = 0; j < 5; j++) {
  for (int i = 0; i < 16; i++) {
    
    buttons[mc] = new Button(i*55+25, j*55+25, 50, 50);
    mc += 1;
}
}
}


void draw() {
  background(255);
  // Show all the buttons
  for (int i = 0; i < buttons.length; i++) {
    buttons[i].display();
  }

// Calculate how much time has passed
  int passedTime = millis() - savedTime;
  // Has time passed?
  if (passedTime > totalTime) {
    
    
 
  rx += 1;
    mx= rx % 16;
    savedTime = millis();// Save the current time to restart the timer!
} 

     if (buttons[mx].on == true){
  println ("EVENT A" +mx);}  
  if (buttons[mx+16].on == true){
  println ("EVENT B" +mx);} 
  if (buttons[mx+32].on == true){
  println ("EVENT C" +mx);} 
  if (buttons[mx+48].on == true){
  println ("EVENT D" +mx);} 
  if (buttons[mx+64].on == true){
  println ("EVENT E" +mx);}
    fill(255,0,0);
    rect(mx*55+25,0,50,20);
    
}

void mousePressed() {
  // When the mouse is pressed, we must check every single button
  for (int i = 0; i < buttons.length; i++) {
    buttons[i].click(mouseX, mouseY);
  }
}


class Button  {    

  // Button location and size
  float x;   
  float y;   
  float w;   
  float h;   
  // Is the button on or off?
  boolean on;  

  // Constructor initializes all variables
  Button(float tempX, float tempY, float tempW, float tempH)  {    
    x  = tempX;   
    y  = tempY;   
    w  = tempW;   
    h  = tempH;   
    on = false;  // Button always starts as off
  }    

  void click(int mx, int my) {
    // Check to see if a point is inside the rectangle
    if (mx > x && mx < x + w && my > y && my < y + h) {
      on = !on;
    }
  }

  // Draw the rectangle
  void display() {
    rectMode(CORNER);
    stroke(0);
    // The color changes based on the state of the button
    if (on) {
      fill(175);
    } else {
      fill(0);
    
    }
    rect(x,y,w,h);
  }
  
} 

In the second version I put the trigger if statement into the coundown timers loop. That makes the event only being triggered once, but then the whole sequencer is one step behind. I’d like to keep my code as simple as possible but i just can’t get my head around this.
It would be verry nice if someone could help me with this issues. Thanks.

int savedTime;
int totalTime = 250;

// An array of buttons
Button[] buttons = new Button[80];

int mc = 0;
int rx = -1;
int mx= 0;

void setup() {
  size(1200, 400);
  savedTime = millis();
  // A loop to evenly space out the buttons along the window
  for (int j = 0; j < 5; j++) {
  for (int i = 0; i < 16; i++) {
    
    buttons[mc] = new Button(i*55+25, j*55+25, 50, 50);
    mc += 1;
}
}
}


void draw() {
  background(255);
  // Show all the buttons
  for (int i = 0; i < buttons.length; i++) {
    buttons[i].display();
  }

// Calculate how much time has passed
  int passedTime = millis() - savedTime;
  // Has time passed?
  if (passedTime > totalTime) {
    
    
if (buttons[mx].on == true){
  println ("EVENT"+1 +mx);}  
  if (buttons[mx+16].on == true){
  println ("EVENT"+2 +mx);} 
  if (buttons[mx+32].on == true){
  println ("EVENT"+3 +mx);} 
  if (buttons[mx+48].on == true){
  println ("EVENT"+4 +mx);} 
  if (buttons[mx+64].on == true){
  println ("EVENT"+5 +mx);}
  rx += 1;
    mx= rx % 16;
    savedTime = millis();
} // Save the current time to restart the timer!
    fill(255,0,0);
    rect(mx*55+25,0,50,20);
    
}



void mousePressed() {
  // When the mouse is pressed, we must check every single button
  for (int i = 0; i < buttons.length; i++) {
    buttons[i].click(mouseX, mouseY);
  }
}

class Button  {    

  // Button location and size
  float x;   
  float y;   
  float w;   
  float h;   
  // Is the button on or off?
  boolean on;  

  // Constructor initializes all variables
  Button(float tempX, float tempY, float tempW, float tempH)  {    
    x  = tempX;   
    y  = tempY;   
    w  = tempW;   
    h  = tempH;   
    on = false;  // Button always starts as off
  }    

  void click(int mx, int my) {
    // Check to see if a point is inside the rectangle
    if (mx > x && mx < x + w && my > y && my < y + h) {
      on = !on;
    }
  }

  // Draw the rectangle
  void display() {
    rectMode(CORNER);
    stroke(0);
    // The color changes based on the state of the button
    if (on) {
      fill(175);
    } else {
      fill(0);
    
    }
    rect(x,y,w,h);
  }
  
} 

You may try out my “Countdown.java” file: :smiley_cat:

If you need, you can create an instance of Countdown for each of your sequencers, if they have diff. firing times. :nerd_face:

thanks. Will this work in android mode?
Edit: Does not work out of the box in android mode.

As long as Android has access to the Java’s imports below, I don’t see why it wouldn’t: :coffee:

import java.util.Timer;
import java.util.TimerTask;
  1. Timer (Java SE 10 & JDK 10 )
  2. TimerTask (Java SE 10 & JDK 10 )

Hm, I can run the sketch in java mode but i get an error about:

import gotoloop.countdown.Countdown;

No library found for gotoloop.countdown
But it’s working.

In android mode it says
The import gotloop can not be resolved.

where do i need to put the files? I don’t know too much about importing things and .java files…
thanks

Never used Android Mode. But here are some guesses: :flushed:

In “Countdown.java”, comment or erase: package gotoloop.countdown;.
In “CountdownClass.pde”, comment or erase: import gotoloop.countdown.Countdown;.

Ok, got it working in both modes.

In android I needed to comment out the line

getSurface().setTitle(countdown.done? END : AWAIT);

in draw(), too, because setTitel doesn’t seem to exist in android mode.
Thanks; still have to try to adapt that for my needs; as I’m not so familiar with the syntax,…

  1. When start() the Countdown, just keep checking its field done.
  2. When it becomes true, it means the delay (in milliseconds) had transpired.
  3. And now you know you can execute your delayed task.
  4. You can reinvoke start() to re-ignite the Countdown all over again.

thnx, i start to understand. got a loop now.

Hm, is it possible to change WAIT_TIME on the fly from draw()? I’d like to implement a bpm slider.

Edit:

Ok, I see it’s

countdown.delay = somenumber;

When we invoke the Countdown::start() method, it uses the current value stored in its field delay in order to instantiate Timeout; which is btW a subclass of TimerTask. :nerd_face:

t.schedule(task = new Timeout(), delay);

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();
    }
  }
}

Thanks. I’ll have a look. I’m using your Countdown Class on Android with a slightly improved version of that sequencer and it works ok. At faster tempi there is some noticeable jitter. But for me thats ok. Shedmusic is working on a timer for android so I might switch over to that one , once he has got it ready.
Meanwhile I’m jamming with yours, it’s fun.

Later I’m gonna merge the classes Countdown & Interval as 1 library class. :cowboy_hat_face:

For now, you can pick which 1 works best for your Android sketch. :robot:

@GoToLoop

Hm, but how do I set the tempo with this one?
With countdown i just set a new countdown.delay time on each cycle, but I don’t know how to do that with interval.

It’s exactly the same as the previous field delay. :money_mouth_face:

Only this time, field period represents a continuous Timeout::run() calling back, which sets done to true each time period milliseconds had transpired. :nerd_face:

Well; i tried .period but it didn’t change.It’s not so easy for me. Could you give a simple example?
I’ve implemented a tempo slider with Countdown but can’t do it with Interval.:construction_worker_man:

So the waiting time needs to be another milliseconds value each round, right? :open_mouth:

In this case, perhaps the original class Countdown is better for your sketch. :thinking:

If you wanna keep the new class Interval, you’re gonna need to re-invoke start() every time you change period to another value. :sweat:

B/c once Time::scheduleAtFixedRate() is called, changing the value of Interval::period won’t affect that particular schedule anymore; and another 1 is needed for the newest period value. :zipper_mouth_face:

https://Docs.Oracle.com/javase/10/docs/api/java/util/Timer.html#scheduleAtFixedRate(java.util.TimerTask,long,long)

I think I’ll keep it like it is, just wanted to try it out if it improves timing.
I just keep reading through code and I guess what it does. With this approach and the help of this forum I did a little Music-Sequencer. Can’t believe it.:man_cook:

1 Like

@sensn
Great to see you have a working sequencer :grinning:
I’m reading a lot about MIDI at the moment but hope to have some kind of clock soon. Going to have a good look at gotoloops clock too.