Is it possible to get millisecond accurate renders with animated Processing sketches?

And the key difference between that blog post and what @leatherbird wants is that the blog post is talking about real time applications, whereas

and so the frames saved will be played back by a video player at a fixed 30 fps and therefore they need to be generated at an equally rigid time sampling which is provided by using frameCount and not millis(). And generated entirely independently of whatever bpm the music is using.

3 Likes

I commend your answers … and your patience! :smile: Some really good points and information shared above. I hope the poster will take them all on board.

Oh! I see now. Thank you.
My apologies. I was frustrated because merely responding:
float animationTime = frameCount / 60.;
by itself was awfully vague, and waxing about Pixar and persistence of vision didn’t help either, and seemed condescending. But in a context of code, this is starting to make sense. Again, thank you. Much appreciated.

At my current skill level, understanding how to use this is proving to be a challenge for me.

I’ve been stumbling a bit trying to figure out how to use your beat floating point with a timer.
But I might be starting to get it. Would you write a timer this?

float fps = 30;
float tempo = 128;
float timerLimit = 4.0;
float savedTime;

void setup() {
  size( 200, 200 );
  noStroke();
  frameRate(fps);
}

void draw() {
  background(0);
  float t = frameCount / fps;  // time in seconds
  float beat = t * tempo / 60;   // time in beats

  float passedTime = beat - savedTime;

  if (passedTime > timerLimit) {
    fill(255);
    circle(width/2, height/2, 50);
    savedTime = floor(beat);
  }
  fill(255, 0, 0);
  text(beat + 1, width/2, height/2);
}

Am I correct here about having to floor the savedTime?

Given that we don’t know anything about your background or skill level in animation or programming or music, any answers you get will almost certainly be either already blatantly obvious to you or confusingly vague. You simply have to excuse the former and ask questions about the latter. I assure you, no condescension is intended – I simply don’t know how much you already know, while at the same time I’m trying to figure out what and how I want to say it.

I’m not quite sure how you want to use that timer, so let’s back up a little. My simple definition of “beats” is coming from a computer programmer mind-set where everything is very lock-step rigid in terms of time. That works great if the music is computer generated or is strictly quantized, but is far too simplistic if this is a recording of a live performance where the beat can wander all over the place either through human imprecision or intentionally for artistic effect.

For the general case, I would imagine you want to have an array of timestamps, measured from the start of the song, with a count of the number of beats in each interval. If the drummer tends to speed up at the start of each 64-beat bridge section and slow as they leave, you can put an extra timestamp 32 beats in and compute the actual bpm separately for each section. If the music is quantized, you can stick with the strictly linear function of time, but you’ll still want the timestamp intervals to drive your animation.

Give yourself a global variable int interval that tracks which section you’re in. float timestamp[i] tells you the start time of each interval (include interval[0] = 0). Include a timestamp for each time that you want to change your animation. At the start of draw() after computing time t, you advance your interval:

if( t > timestamp[ interval+1 ] ) interval++;

Then you can have a switch statement that controls what animation code you run for each interval:

float intervalTime = t - timestamp[ interval ];   // starts from 0 at the start of each interval
switch( interval ) {
  case 0: fadeIn( intervalTime );  break;
  case 1: dramaticFlash( intervalTime );  break;
  case 2: wavyLines( intervalTime );  break;
}

and so on. Maybe define intervalTime as a global so you don’t have to pass it around everywhere. Likewise, you might want to make beat a global.

beat is a linear (or approximately linear for live music) function that rises 128 beats vertically for each 60 second horizontally. beat % 4 turns it into a triangle wave rising from 0 at the start to just under 4 at the end of each 4/4 time measure. floor( beat % 4 ) would count from 0 to 3 for each beat in each measure. floor( beat / 4 ) would id which measure number you’re in.

colorMode( HSB, 1, 1, 1, 1 );
fill( 1., pow( 1.- (beat % 1), 2 ) );
circle( x, y, r );

would give you a pulse per beat that quickly fades out.

1 Like

I see.
So a metronome could look something like this?

float fps = 30;
float tempo = 128;

void setup() {
  size( 200, 200 );
  noStroke();
  frameRate(fps);
  colorMode(HSB, 1, 1, 1, 1);
  rectMode(CENTER);
}

void draw() {
  background(0);
  float t = frameCount / fps;  // time in seconds
  float beat = t * tempo / 60;   // time in beats

  fill(0, 1, 1);
  text(floor(beat % 4) + 1, width/2+ 45, height/2);
  text(floor(beat/4) + 1, width/2 - 50, height/2);
  println(floor(beat/4) + 1, floor(beat + 1));

  if (floor(beat % 4) == 0) { // Beat one
    fill(1., pow(1.-(beat%1), 2));
    circle(width/4, height/4, 50);
  }
  if (floor(beat % 4) == 1) { // Beat two
    fill(1., pow(1.-(beat%1), 2));
    circle(width * 0.75, height/4, 50);
  }
  if (floor(beat % 4) == 2) { // Beat three
    fill(1., pow(1.-(beat%1), 2));
    circle(width/4, height * 0.75, 50);
  }
  if (floor(beat % 4) == 3) { // Beat four
    fill(1., pow(1.-(beat%1), 2));
    circle(width * 0.75, height * 0.75, 50);
  }
}

I was perplexed by how to find a particular beat in each measure. For example beat 3.
I realized counting every 3 beats wouldn’t work in a song with 4 beats per measure, but couldn’t figure out how to get around that.
My first thought was to start a timer every 4 beats, and then do an action on beat 3… but kept getting confused how to work it.
floor(beat % 4) is clearly a better solution.

(sigh…) Modulo always confuses me.
I understand the textbook definition of what the modulo operator does, but my math skills aren’t strong enough to understand what it’s capable of, or how/when to use it creatively.

It would never have occurred to me that it could find a beat for me.
Even now looking at it, I’m having trouble comprehending how it’s doing this.
But that’s on me to research more. I appreciate all your help.

Oh, and by the way, I’m only working with quantized music.
A recording of live musicians is obviously wildly beyond my skill set.

Yep, that works. Maybe add int iBeatNumber = floor(beat%4)+1; at the top to save repeating it below.

I also recommend that you use

translate( width/2, height/2 );
scale( height / 2 );

circle( -0.5, -0.5, 0.25 );

to run your coordinates from -1 to 1 with 0,0 in the middle of the screen. That way you can change your size() between a small window and fullscreen and your content will scale appropriately.

Wait, i don’t understand.
I thought the point of using width and height was so that it would scale.

edit: Oh, do you mean so it keeps the square aspect ratio?

I mean so you don’t have to pepper your code with multiplying everything by width and height. If you

translate( width/2, height/2 );
scale( height/2 );

just once at the top of draw(), everything below it will be in a coordinate system that ranges from -1 to 1 vertically and whatever the equivalent is for the width. Or scale by height/20 for -10 to 10 if that feels better for you. That way you can say size(200,200); or size(3840,2080); for a 4k image and your drawing commands can all be consistent and simple numbers like circle( 0.5, 0.5, 0.1 );. Makes for less code. Easier to write and easier to read.

Let me add that you will also have to use noStroke() or set the strokeWeight to something small as well since the default is for it to be 1 which will now be huge on the screen. But it’s still easier to do that once than to multiply everything by width and height.

Is there a clever way of finding beats and the fractions of beats, say… an 8th note?
My understanding of what is happening with floor(beat % 4) is that it makes finding 0.5 beats impossible.
The solution I discovered last night was doubling the tempo and using floor(beat % 8)
At 256 bpm:
0 is beat 1,
2 is beat 2,
3 is beat 2.5 (The “And” of beat 2), etc…

Is there a smarter way?

2 Likes

No, that’s the smart way to do it. You got it.

2 Likes