Yeah, I forgot we do use that now, when hosts make it available (which some don’t). Still a little complex, having to compute backwards into our graph from the current buffer start to when the most recent bar/beat occurred. And the code assumes that there hasn’t been a time sig or tempo change since the last bar. If it helps, here’s the code we use:
if (data.processContext != NULL)
{
//
Steinberg::uint32 state = data.processContext->state;
if ( isSamplePosKnown &&
((state & ProcessContext::kPlaying) != 0) &&
((state & ProcessContext::kProjectTimeMusicValid) != 0) &&
((state & ProcessContext::kBarPositionValid) != 0) &&
((state & ProcessContext::kTempoValid) != 0) &&
((state & ProcessContext::kTimeSigValid) != 0) )
{
// Compute last bar#, and fraction of a bar since then
float beatsPerQNote = (float)(data.processContext->timeSigDenominator) / 4.0;
float barsPerQNote = beatsPerQNote / data.processContext->timeSigNumerator;
float lastBarNumberFloat = data.processContext->barPositionMusic * barsPerQNote; //barPositionMusic is in qNotes!
float barNumberIntPart = floor( lastBarNumberFloat ); // -2.3 becomes -3.0, as we want!
int32 lastBarNumber = (int32)barNumberIntPart;
/* TODO: what if this is not zero?
float barNumberFraction = (lastBarNumberFloat >= 0) ?
lastBarNumberFloat - barNumberIntPart : // if >= 0
barNumberIntPart - lastBarNumberFloat; // if < 0
*/
// Compute last beat# in current bar, and fraction of a beat since then
float qNotesBackToLastBar = data.processContext->projectTimeMusic - data.processContext->barPositionMusic;
float currentBeatFloat = qNotesBackToLastBar * beatsPerQNote;
float currentBeatIntPart = floor( currentBeatFloat ); // always >= 0
float currentBeatFraction = currentBeatFloat - currentBeatIntPart;
Steinberg::uint16 lastBeatNumber = (Steinberg::uint16)currentBeatIntPart;
// Increment bar and beat, since calculations is 0-based, but numbers are 1-based!
lastBarNumber++;
lastBeatNumber++;
// Are we at a new bar or beat?
if ((lastBarNumber != lastRecordedBarNumber) || (lastBeatNumber != lastRecordedBeatInBar))
{
// Yes; Record the new bar/beat numbers
lastRecordedBarNumber = lastBarNumber;
lastRecordedBeatInBar = lastBeatNumber;
// Now, we have the most recent bar# and the most recent beat#,
// and the fraction of a beat since the last beat.
// We need to determine how long ago the last beat occurred, in samples.
// To do that, we need to determine the samples per beat,
// which is the samples per second (sample rate) divided by the beats per second.
// The beats per second equals the quarter notes per second times the beats per quarter note.
// The quarter notes per second equals the tempo divided by 60, and
// the beats per quarter note equals time signature denominator divided by 4. So...
float qNotesPerSecond = data.processContext->tempo / 60.0;
float beatsPerSecond = qNotesPerSecond * beatsPerQNote;
float samplesPerBeat = data.processContext->sampleRate / beatsPerSecond;
//
float samplesSinceLastBeat = samplesPerBeat * currentBeatFraction;
Steinberg::uint16 samplesSinceBeatInt = (Steinberg::uint16)samplesSinceLastBeat;
long sampleAtBeat = samplePos - samplesSinceBeatInt;
// Now we have the sample position of the last beat, which we use to mark it on our sample-based graph