Inaccurate time intervals for a Metronome

Hello. I just started working on a metronome app that I would like to create. I’m having trouble with triggering the beat on precise time intervals. Besides that, Processing’s graphics are inconsistent too.

The logic is simple. I want to have a metronome running at 120 beats per minute, which precisely translates to 1 beat per 500,000,000 nanoseconds. What I’ve tried to do is store the system timer during time of initialisation, then increment that by 500,000,000 and trigger a tick every time the system time equals that incremented time.

For simplicity’s sake, I have replaced the tick with a print statement that prints out ‘Boom{n}’ for the nth tick since initialisation.

The problem: There is a significant lag between the time that it is suppose to trigger and the time that it actually does. This can be seen through the print statements (I used long variables to calculate this). On average, the gap between two ticks was ~530,000,000 which is almost a 0.03 seconds lag per tick. Over time, this lag accumulates and the accuracy is unacceptable for a metronome.

Here’s the code:

long initSysTime;
int c = 0;

float bpm = 120;
float interval = 1e9 / (bpm / 60);
float nextTime;



void setup() {
  size(600, 600);
  //frameRate(120);  
  initSysTime = System.nanoTime();
  nextTime = initSysTime + interval;
}


void draw()  {
  background(0);
  float temp = System.nanoTime();
  
  //ellipse(width/2, height/2, height/2, height/2);
  // This isn't being drawn while testing to better see the ellipses that Processing doesn't draw when triggered, despite the trigger going off, as seen through the print statements in the if statement
  
  if(temp == nextTime)  {
    nextTime = temp + interval;
    ellipse(width/2, height/2, height/2 + 0.016*height, height/2 + 0.016*height);
    c++;
    println("Boom" + c);
    println("SysTime = " + temp);
  }
}

I had a look at this topic too: Issues creating a metronome
The best suggestion redirected me to a countdown library (https://github.com/dhchoi/processing-countdowntimer), whose source code I looked into and over here too, System.nanoTime() was used for precision.

Any help is sincerely appreciated. Thank you for taking the time to read this.

1 Like

From what I found during my current project print() and println() are kinda slow. If I use them to debug during some big for loops they seem to take ages while they only need about a second if run wihout.

Also I’m not sure about this.

because if your program misses that percise moment due to lag or whatever it won’t trigger at all. I’m unsure in such situations so I find myself using this:

if(temp >= nextTime) {

quite often.

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

1 Like

I’ve tried that method too. It gives me a similar result.

I forgot to mention that while checking for accuracy, I ran it alongside an accurate metronome at 120 bpm and this confirmed that the lag was actually happening – my program was playing it slower than 120 bpm.

Hm
If you want it really accurate I guess you will have to run the timing part in a different thread with high priority and in your draw() only check for a shared variable to find out when to show a beat.

Oh. I have no idea how to go about doing that, or what that really means. Could you redirect me to a source to know more about this?
PS – I’m new to Processing, and this is my first project on it.

Please have a look at these links:

https://forum.processing.org/two/discussion/9432/having-an-event-occur-every-n-milliseconds
https://stackoverflow.com/questions/4544197/how-do-i-schedule-a-task-to-run-at-periodic-intervals

Kf

1 Like

Thank you, this seems very helpful.
I shall implement this and let you guys know if it is precise or not.

Thanks @GoToLoop. A link that @kfrajer provided is where you’ve implemented the same class that you’ve redirected me to here.

Using TimerTask or another Thread for this is a daft idea that’s just going to tie the OP in knots trying to get things to synchronize. Please note the “I’m new to Processing” - consider half the seasoned developers on here can’t do multi-threaded programming right! :wink:

There are a few things wrong with the initial code, but not that can’t easily be fixed. Try -

int c = 0;

float bpm = 120;
long interval = (long) (1e9 / (bpm / 60));
long nextTime;

void setup() {
  size(600, 600);
  nextTime = System.nanoTime() + interval;
}

void draw()  {
  background(0);
  if(System.nanoTime() >= nextTime)  {
    nextTime = nextTime + interval;
    c = 255;
  }
  if (c > 0) {
    fill(c);
    ellipse(width/2, height/2, height/2 + 0.016*height, height/2 + 0.016*height);
    c -= 30;
  }
}

Note

  • Don’t use floats for time - keep it in longs
  • Make sure you follow @MxFxM advice about time >= nextTime, but you don’t need temp, because …
  • To get fixed rate you need to add the interval to your previous nextTime, not the current time, otherwise you accumulate errors.
  • I needed to add the if (c > 0) section to animate down the fill colour, or sometimes the default renderer output doesn’t show for me - seems some calls to draw() don’t make it on screen! (I repurposed your count variable for this :smile: )
4 Likes

Wow @neilcsmith this is an elegant, simple solution and works flawlessly. The timing is on point, as are the animations. Thank you!

On a different note, I skimmed through your website, and your work is very inspiring (mentioning this here as I don’t know how else to contact you).
Do you document any of your installations via audio/video?

1 Like

No problem! Glad it does what you want. I’ve done a lot of animation stuff - putting things in separate threads is rarely the answer for accurate timing!

Thanks for your kind words on my work. There’s an email link on the front page. Perhaps too hidden! :smile: There are videos on a few of the projects - eg. Meeting Point and Sound Pool - need to get up-to-date on the old documentation. There’s also this video of a talk/demo on PraxisLIVE, which was used for most of the work.

PM me here or email me if you want.

3 Likes

Watched the video on PraxisLIVE – really cool stuff! I had no idea algoraves were a thing!

Sorry for the delayed response, I’ve been travelling and afk for a while.
I’ll email you a snapshot of a project I’m working on soon, I’d like to hear your thoughts on it if it isn’t too much of a bother.