Sample accurate host beat sync

Hiya, I needed a way to determine when the host beats occur during block processing in order to trigger an event. I thought I’d post this here because it took me a little while to figure it out and maybe it will be helpful to someone. All the examples I saw looked like they were doing things per-sample and either setting an LFO phase or something similar and while I figured out a way to use a per-sample method to determine the beat changes, it seemed pretty inefficient. I’m sure you people who have been developing plugins for a long time probably have better ways to do this, but here’s one way you can do this per-block:

First, calculate the total ppq time for the current block, nFrames is the number of samples in the block, of course

double blockPPQ = nFrames / GetSamplesPerBeat();

Next, and this is the trick to find if the beat changes during this block, use mPPQPos (VST projectTimeMusic). Note numBeats is the beat divisor, e.g. it would be 1 for quarter note .5 for eighth or 2 it would repeat every 2 beats. You may need to get even more complicated by using the mTimeInfo.mNumerator and mTimeInfo.mDenominator to determine true pulses but in my case, I could just call 1 a beat no matter what the time signature. You also might want to look into mTimeInfo.mLastBar to see which beat of the measure this is, depending on your use case.

double beatChange = blockPPQ - fmod(mTimeInfo.mPPQPos + blockPPQ, numBeats);

If beatChange is >= 0 there is a change during this block

if (beatChange >= 0) {

… you may just want to set the beatChangeSample, process and then do what you need during the processing:

long beatChangeSample = round(beatChange * GetSamplesPerBeat());

In other words:

double beatDiv = 1.;
double samplesPerBeat = GetSamplesPerBeat();
double block = nFrames / samplesPerBeat;
double beatChange = block - fmod(mTimeInfo.mPPQPos + block, beatDiv);
long beatChangeSample = round(beatChange * samplesPerBeat);
for (int s = 0; s < nFrames; ++s) {
  if (s == beatChangeSample) {
    // beat change, do stuff
  }
}

Hope that helps someone!

2 Likes

Hi kzantow, thanks for sharing that.

So far, I’ve been using the function GetSamplePos() to align the peaks of a tremolo to the beats (or 8th, 16th note, &c.) of a song. Along with GetSamplesPerBeat() it’s not very difficult to find the beats, but of course things get complicated with meter and tempo changes.