@Christoph-Hart found the culprit:
// if this is non-zero it means that the buffer coming // from the DAW was split into chunks for processing // so we need to update the playhead to reflect the // "real" position for the given buffer if(offsetWithinProcessBuffer != 0) { newTime.timeInSamples += offsetWithinProcessBuffer; newTime.timeInSeconds += (double)offsetWithinProcessBuffer / processingSampleRate; const auto numSamplesPerQuarter = (double)TempoSyncer::getTempoInSamples(newTime.bpm, processingSampleRate, 1.0f); newTime.ppqPosition += (double)offsetWithinProcessBuffer / numSamplesPerQuarter; }