Can a DAW loop within a sample buffer block in processBlock?

Hello :slight_smile:

Just wondering, could a DAW loop happen in the middle of a sample buffer in processBlock?

Say you want to loop 4 bars at 120 bpm, that’s a precise 8000ms loop. If your sample rate is 44100, that means you are looping over 352800 samples.

But if your sample buffer size is 512, the loop is over 689.0625 blocks, which means on the 690th block, you should technically handle the loop after 32 samples within the block.

Is that correct, or would the DAW “round” it to the block size and actually loop 352768 samples (7999ms) instead?

If there is no rounding, how can you handle (or even know) if the DAW is loop within the block of samples?

Thank you!
Simon

Gut feeling is that the DAW would do 689 blocks @ 512 samples and single 32 sample block, but the only way to really know for certain would be to test. But then you’d really need to test in every DAW to know for actually certain how this is handled.

This is one of those things that is for sure going to be implemenation specific, and one DAW may do it one way, another in an entirely different manner. :person_shrugging:

could a DAW loop happen in the middle of a sample buffer in processBlock?

Of course, why wouldn’t it? If the buffer size is, say 1000 samples and the loop is 10 001 samples, then the loop would first fill 10 buffers, put the 10 001:th sample in the 11:th buffer and fill it up with the 999 first samples of the loop and so on.

A more interesting question would be how the daw handles the loop point. If you just stitch two audio samples together, you’re likely to hear a click or some other artifact at the stitch point. That’s why you normally fade out the first sample while simultanesously fading in the new, keeping the perceived volume stable. But how do you do that with a loop? Do you play beyond the nominal loop point, while fading out and mix that with a fade in of the start of the loop? Or the opposite, fade in before the nominal loop start and mix that with a fade out before the loop end point? Or use a mix of these two approaches?

Thank you both very much for the replies.

@ asimilon → that would be ideal, but I am afraid oxxyyd has a point, if a DAW needs to use x buffers + 1 sample, it is not going to call processBlock on a block of 1 sample, is it?

@ oxxyyd → if that is the case, then how are plugin developpers meant to know on which sample the loop is happening?
I can see you imagine an effect plugin, where this would not matter much (except for the fades as you mentioned), but what about a synth which is tempo-based?

Thanks
Simon

Why wouldn’t it? That’s allowed by the plugin formats, and plugins shouldn’t have any problems with that.

Without testing it’s impossible to say how any particular DAW is handling this.

For your specific use case though, AudioPlayHead::PositionInfo has a getLoopPoints method. I suppose you could take the current getPpqPosition, convert the block length to PPQ[1] and add it to that, if the ppqEnd in the loop points is less than calculated block end then you have to handle a loop within that process block.

[1] this nomenclature has always frustrated me; Parts per Quarter Note was historically a measure of how much a quarter note is divided up into, i.e. the resolution at which a sequencer was able to operate. Unless of course I had misunderstood somewhere along the line!

Why would the plugin developer want to know the loop sample in the first place? You (usually) can’t determine where e. g. bars or beats or other musical contents are situated in a block, so what’s your use case for wanting to know the loop position?

Don’t really know what you mean with a “synth which is tempo-based?” Like an ordinary drum machine?

I chime in on asimilons suggestion to check if the AudioPlayHead::PositionInfo carries enough information for your plug-in to do what you want.

Thank you all - very helpful! :))

@xenakios → yep that would work fine with me :slight_smile:
But I think like asimilon said, there is no way to really know without testing it.

@asimilon → very good point! I guess I would also need to check if the DAW transport is currently looping (thinking it would be possible to create loop points without having the “loop” enabled in the DAW).

But I think this is the part that really drives me nuts developping plugins: all this info, like the ppqPosition, the current BPM, the loopPoints, the time signature, the famous “lastBarPositionInPpq” - all of these are implemented differently in every DAW, sometimes they are not even implemented at all >_<
Thank you so much Steinberg for making these “optional” in the VST standard - that made a mess IMO :confused:

(and yes I fully agree with the “ppqPosition” naming, it should be called “positionInQuarters” like it is in the tempoEntries on ARA plugins, it has nothing to do with “pulses/parts per quarternote”)

@oxxyyd → yes a drum machine for example, that needs to be in time with the DAW’s metronome, playing together with the beats and bars. Actually one guy made a presentation at the ADC22 about developping a simple metronome plugin and all the struggles that come with it, that was super interesting: Every Beat Counts - Tempo Sync 101 - Tal Aviram - ADC22 - YouTube

Cheers
Simon

@simon Hey man, i had the same problem too,
Everytime I’ve hit the loop point all my calculations after it got wrong,
After a little research (in Ableton & Reaper) I saw that the block size was cut off at the loop point and wasn’t the full one,
For example:
If I set a block size to 512, when it got somewhere near the loop point, I got a size of 216 instead of 512, I used this information to get my calculation right.

This is a code example of how you can know what is the current block size:

void AudioProcessor::processBlock (juce::AudioBuffer<float>& buffer, juce::MidiBuffer& midiMessages)
{
    ....

    auto bufferSize = buffer.getNumSamples();
    ....
}

That way i got away with knowing the loop point, or reading the optional playHead properties and all of those stuff that might not be supported in all of the DAWs

1 Like

As Dannyets said, you can (and should) get the buffer length from the buffer itself. It can even be 0 samples long! What you get in prepareToPlay is the maximum buffer size you can expect, not what you will always get.

1 Like

So I have been doing some testing regards this and wonder if anyone has any knowledge about how to solve one particular issue:

In the most part (I saw this behaviour in Ableton Live, Reaper, Bitwig and Cubase with ASIO Guard disabled):

Let’s say the buffer size for the audio interface is set to 256 samples

Blocks will arrive with the buffer size set to 256
When the loop point arrives the host will call processBlock twice, once with a short block that will go up to the loop end point, and again with the remaining samples needed to make 256 samples from the loop start point.
eg.
256 samples
256 samples
256 samples
192 samples ← up to the loop end point
64 samples ← from the loop start point, these two must be getting stitched together by the host
256 samples
256 samples
etc.

In Cubase with ASIO Guard enabled the block size seems to be 512 samples regardless of the audio interface buffer size.

This causes issues for me when the loop point happens. Is there any way to know that a block is containing the loop and at which point in the block the loop happens?

Actually there is, you can use:

  • getPlayHead()->getPosition()->getIsLooping() to know if looping is enabled
  • getPlayHead()->LoopPoints to get the loop points

Then using the sample buffer size, the current ppq position ( getPlayHead()->getPosition()->getPpqPosition() ), and the BPM, you would know if one of the loop points is “within” your current block :slight_smile:

Cheers
Simon

1 Like