processBlock: samplePosition, getTimeStamp() interpretation

Hi!

I am trying to figure out if I can make use of either the midi message samplePosition or the getTimeStamp() value in the juce::AudioProcessor::processBlock method. My goal is to be able to look at the data associated with a midi message in the processBlock method and understand where it fits in relation to other midi messages (note on/off specifically).

I am, however, at a complete loss as to what the values might mean. From observation/testing, it appears that in the callback AudioProcessor::processBlock, the samplePosition (of the midi metatadata) and midi message getTimeStamp() are always the same when using a plugin inside of Logic Pro X. And the values seem to be from 0 to 127. The header file and documentation for getTimeStamp do indicate that “The exact meaning of this time and its units will vary,…” due to context.

So my specific question is: Can you help me understand what those time values mean in the context of a plugin?

But, from a problem-solving aspect, what I really want to know: Is there a way to know anything about a midi note’s relative time position within the processBlock callback? In other words, when I receive a callback in processBlock telling me the note C# was turned on (note-on), is it possible to know it’s relative position in the overall track?

Thanks!!
Mark

You are correct. Those timestamp values are both representing the same thing, which is the time of the message related to the audio buffer’s sample loop.

The midi note values 0 to 127 represent the pitches of the notes: https://newt.phys.unsw.edu.au/jw/notes.html

The getTimeStamp() is the position local in that processBlock’s audio buffer. To get the sampleposition in your timeline you need to add the position of the audio playhead in that processBlock().

int64 posOfBlock = 0;
if (auto* playHead = getAudioPlayHead())
    if (auto position = playHead->getPosition())
        if (auto samplePos = position->getTimeInSamples())
            posInBlock = *samplePos;

auto globalPosOfEvent = message.getTimeStamp() + posOfBlock;

That’s a bit weird, the getTimeStamp() should be from 0 to buffer.getNumSamples() (excluding).

Some bad hosts might not set the timestamp properly and you may see all as 0.

Some other hosts split the buffer in smaller chuncks so that every note arrives at position 0 as well. This might even lead to blocks with 0 size…

2 Likes

I think tetra was refering to the value of the note event

If you are live monitoring instead of playing back track content, the host probably just uses 0 as the timestamp for the messages since there might not be a reliable way to determine a sub buffer position from the real time MIDI messages. If the time stamp is also 0 even when playing back track content, that’s a bit more mysterious, the expectation would be that the host would be able to determine a sample position inside the buffer.

Thank you!!! That is exactly what I needed. I just tested it out and see that the values are consistent (and increasing). This makes much more sense to me now.

I should not have mentioned the 0 to 127 “information”; that was just a really bad and misleading assumption on my part from observing some of the timestamp values with DBG statements. I obviously did not look at enough values and jumped to an incorrect assumption that they were some kind of midi message value rather than a timestamp. I apologize for that.

Thank you again for getting me going in the right direction.

Mark

Why would that be? Surely you have a different latency than when playing back, but in relation to each other event, they should be placed consistently…

Yeah looks like I assumed wrong how it would work. (I have done the “real time MIDI events at time stamp 0” in my own host because it seemed the most straightforward way to handle it, but I guess I need to look into it a bit further.)

I only have Logic Pro X for testing, but it is clear to me now (with insight/info from Daniel) that the samplePosition/getTimeStamp values are relative to the block and can change with each callback. If I start playback at the beginning of a measure and a note is at that position, that samplePosition is zero. But if I start playback a measure (or some amount) earlier, then the samplePosition > 0. But adding in the playhead position as described by Daniel makes them reliable and consistent. I’m unsure what the time units are; they change if I modify the tempo of the project. But for my use case, I don’t need the units.

Thanks again.