processBlock with float vs double?

whoa, so many levels of abstraction :sweat_smile:

Well, luckily until now nobody suggested to assign an AudioBuffer that way, since like you pointed out it would allocate. :wink:

You can even create your Engine to work with fixed buffers and wrap the call in the processBlock to call in chuncks of the buffer.

But ideally just create the buffer in the size of samplesExpected and use the numSamples value from the original supplied buffer instead of the buffer size of the preallocated. I think that is common knowledge in any preallocated buffer scenarios

1 Like

why?

What I’m currently doing is creating buffers that are preallocated to larger sizes than I need, and in my process Block, I do

void processBlock(juce::AudioBuffer<float>& buffer, juce::MidiBuffer&)
{
    AudioBuffer<float> dryProxy  (dryBuffer.getArrayOfWritePointers(), 1, startSample, numSamples);
    AudioBuffer<float> wetProxy  (wetBuffer.getArrayOfWritePointers(), 2, startSample, numSamples);
}

so you have a super convenient proxy dryProxy & wetProxy to refer to that are the exact length you need and no reallocating is involved

(and also, if you’re breaking into chunks and you have a sample offset startSample, then the proxy buffers will have content from sample index 0 to numSamples-1, super convenient)

Isn’t there a chance that the buffer passed to processBlock can be larger or smaller than samplesPerBlockExpected so even if you checked which was smaller and used that for the number of samples to copy to your storage buffers, you might not always fill them meaning you’ll have a bunch of not-up-to-date samples at the end of the buffer which is going to cause problems.

1 Like

not if you use the proxy buffer method, because the extra samples at the end of your internal buffers are not included in the proxy objects

There’s no reallocation, no. But you are allocating memory on the audio thread there when you create the buffers.

this constructor only refers to a block of preallocated memory and does not hold any data

I think…?

Technically yes. It is ensured by contract, that means a host which wants to send bigger buffers is required to call prepareToPlay again with this bigger buffer size, giving you the chance to reallocate safely.

A host sending bigger buffers than announced can be considered broken, it violates the API.
But sure, if you can add that safety measure to calculate in smaller chuncks in that case then do it. But technically you are not required.

1 Like

Yes you’re right, sorry I missed which constructor you were using!

1 Like

ever since about this wonderful magical constructor, all my processing code got like 300% simpler

Looks like it calls allocateChannels() internally which will only allocate memory if the number of channels you’re using is more than the size of preallocatedChannelSpace which is 32. So as long as you know you’re not using more than 32 channels that’s safe to use.

1 Like

Good to know!

That’s why they eventually invented the dsp::AudioBlock. Moving forward it’s good to replace those internal AudioBuffers with dsp::AudioBlock…

1 Like

Agreed that it would be the hosts fault if the block size wasn’t correct, but if your plugin is the one that causes the crash you’re the one who’s going to get the angry emails from users!

I’d much rather spend a few more hours writing saftey nets than spend 10 minutes answering emails! :joy:

I don’t hear me disagreeing :slight_smile:

Wait I thought an AudioBlock was basically an alias for an AudioBuffer…?

I’m still a bit confused about that

I agree :joy::joy:

Generally an AudioBuffer owns the audio data, with the one exception.
And generally an AudioBlock doesn’t own or allocate audio data, but unfortunately also with one exception.

The idea was to have it really explicit, if a structure owns the audio data or not. You can simply create an AudioBlock or a subBlock from an AudioBuffer to feed it to your processor.

Sadly they built in the backdoor of an dsp::AudioBlock (HeapBlock& b), which will allocate :rage:

1 Like

I guess I can understand the logic there…

but personally, I only use these proxy buffers within the local scope of a function, so it’s always easy to see that they’re declared with getArrayOfWritePointers() because the constructor is never too far above you ¯_(ツ)_/¯