Multiple calls to prepareToPlay in AAX


#1

Hi!

 

We've had problems with the some of our plug-ins in Pro Tools that perform lengthy pre-calculations in the prepareToPlay method. Pro Tools constantly changes the buffer size during scrubbing and the AAX wrapper calls prepareToPlay everytime the block length is modified (see process method defined in juce_AAX_Wrapper.cpp, line 855). The resulting CPU usage increases to the point where the CPU gets exhausted.

 

Is it really necessary to call prepareToPlay in the wrapper? Shouldn't the call to setPlayConfigDetails be sufficient?

 

Best,

Stian


#2

Well, yes, it really is necessary for us to call prepareToPlay - most plugins will use it as an opportunity to initialse sample-rate and block-size specific resources. We can't change that.

It does sound a bit odd that PT would call it so often though - normally a host will only call it once before play starts, or when the sample rate changes. We'll take a look when we get a moment in case there's a workaround we could do for this.

But it's not clear from your comment whether you could just ignore changes to the buffer size and avoid recalculating your resources? I guess not, as that sounds like an easy fix.


#3

Thanks, Jules. The current work-around is to check if the sample rate is identical and don't do the recalculations. However, I've always reset the internal state in prepareToPlay (e.g. cleared delay lines), but that isn't desirable when changing only the buffer size for obvious reasons. That might be a misunderstanding from my side, though. Is the intention to only clear the state in the reset method?


#4

No, sounds like you're doing things right, and PT is just stopping and starting things too often..


#5

But PT is not stopping or starting, it merely changes the buffer size. The wrapper detects the change of buffer size and calls prepareToPlay, which then again causes the plug-ins to reset the internal state, hence causing audible clicks. The plug-ins can't really rely on the blockSize parameter anyway, some VST hosts like e.g. Fruity Loops and Audacity change the buffer sizes constantly.


#6

I think stian is right, the call to prepareToPlay should only happen, if the provided blocksize is bigger then the inital maximum blocksize.

My plugin also resets the filters & delaybuffers etc. when prepareToPlay is called. 

Would be interesting to know which ProTools Version & Sound Device configuration is used. 


#7

Well chkn, I don't understand why you couldn't add the logical check for this increasing buffer size in your prepareToPlay() implementation?


#8

Because you don't know whether or not to reset the internal state... :)


#9

exactly!


#10

sorry for bumping, but i think calling prepareToPlay from the audio callback is still wrong, none of the other wrappers behave this way. It should only happen if the blocksize is bigger, than the maximum which was defined by the inital prepareToPlay call.

prepareToPlay in my case it resets all internal buffers etc... (and i think is also what other plugins do)

I think the wrong behavior was induced here http://www.juce.com/forum/topic/aax-wrapper-various-fixes

 

 


#11

We hear you - will investigate this soon, but probably not until next week..


#12

Hi there,

OK, finally I had some time to take a deeper look into this. And yes, I can reproduce it! Whenever you scrub *backwards* using the scrubbing tool, Pro Tools calls prepareToPlay() a few times with different (smaller) buffer sizes. The subsequent calls to the audio callback will then also use that smaller buffer size. As soon as you stop scrubbing backwards, everything reverts back to the original buffer size.

I think there is nothing we here can do about it - if we are in the prepareToPlay() callback, there is no way to tell whether we are scrubbing or not, it seems to be a weird quirk of Pro Tools that we have to live with.

However please keep in mind that the buffer size passed into prepareToPlay() is only a hint anyway. It doesn't come with a promise that the next audio callback will actually use that buffer size. It might be called with a smaller buffer size even without a prepareToPlay() callback in between that announces that buffer size change -- hosts are allowed to do that.

So I think the best strategy would be to implement prepareToPlay() in such a way that you just ignore the call to it whenever it's safe to do so.

For example: the first time prepareToPlay() is called, it is passed a buffer size of 1024, so you do your initialisation and allocate your resources. Then, the user scrubs in ProTools, and prepareToPlay() is called again with other numbers like 256, 128, ... which are smaller than the original first call - so it's safe to just ignore it. You already allocated everything! An interesting detail is that ProTools never calls releaseResources() in between those spurious prepareToPlay() calls, so it's actually safe to ignore those additional calls.

I'll add a comment to prepareToPlay() saying that it might be called spuriously and that you should keep that in mind when implementing it.

Is that satisfactory?


#13

Actually, I just noticed the documentation of prepareToPlay() is already saying that:

/** Tells the source to prepare for playing.

    [...]

    Note that this method could be called more than once in succession without
    a matching call to releaseResources(), so make sure your code is robust and
    can handle that kind of situation.
*/

so yeah, please do what the documentation says! :-)


#14

Interestingly we've just been looking into similar issues in AU and VST3. It appears hosts can basically do what they want with these methods and call the processBlock with any number of samples (up to the number declared in prepareToPlay).

So as Timur says it seems the most sensible thing to do is not clear state or stop voices etc. unless you get a call to AudioProcessor::reset().

Does that align with everyone else's findings?


#15

Sorry I absolutely do not agree!

Pro Tools calls prepareToPlay() a few times with different (smaller) buffer sizes

No the problem is, the WRAPPER calls prepareToPlay in the process block not ProTools!

​
void process (float* const* channels, const int numChans, const int bufferSize,
                      const bool bypass, AAX_IMIDINode* midiNodeIn, AAX_IMIDINode* midiNodesOut)
        {
......

if (lastBufferSize != bufferSize)

                {

                    lastBufferSize = bufferSize;

                    pluginInstance->setPlayConfigDetails (pluginInstance->getNumInputChannels(),

                                                          pluginInstance->getNumOutputChannels(),

                                                          sampleRate, bufferSize);

                    pluginInstance->prepareToPlay (sampleRate, bufferSize);

                }

So i can't decide between the "original" call, and the call from the AAX process() function! 

So when its equal, what prepareToPlays blocksize is, remove this part from the process(), its needless!

Neither the AU or the  VST-Wrapper do such a blocksize-size check!

 

 

 


#16

I'm with chkn on this and it's a pity if we have to maintain our own fork just because of this as we're doing now. Once again:

  1. The wrapper calls prepareToPlay, not PT
  2. By doing so, there is absolutely no way for the plug-in to know whether or not to clear the internal state of the plug-in (delay lines, etc.)
  3. The other wrappers don't behave this way.

I know that we cannot rely on the block length parameter in the prepareToPlay method and that's actually an argument why the extra call to prepareToPlay is superfluous...


Best,

Stian


#17

Thanks Stian for the support! I think this is just a misunderstanding, that "wrong" prepareToPlay calls come from the AAX-Wrapper process() function

I think the wrong behavior was suggested here http://www.juce.com/forum/topic/aax-wrapper-various-fixes

 


#18

Hi guys,

OK, I just committed a fix that I hope will make everyone here happy...

Instead of calling prepareToPlay() from the wrapper's process method every time PT decided to use another buffer size, it's now only being called if the buffer size is *larger* than the one that was used the last time prepareToPlay() was called.

In this way, prepareToPlay() will be called every time you might actually need to allocate stuff, but it will never be called if PT decides to use smaller buffer sizes now and then (as it does during scrubbing).

This should get rid of the problem you folks are having here without changing or breaking anything else.


#19

Thanks, Timur! That's exactly what I was hoping for... :)

Best,

Stian