AAX wrapper: various fixes


Prevent asserts when declaring > 8 channels

I’ve got a plug-in which declares > 8 channels (VST…); when initializing the AAX version of this plug-in, it asserts.

Fixed by not calling createDescriptor() nor descriptor.AddComponent() if channelConfigs[i][0] > 8 or channelConfigs[i][1] > 8.

Parameters depending on the number of channels

I’ve got a plug-in whose parameters (automatable or not, names, etc.) vary depending on the i/o channel count of the plug-in.

Until now, with the RTAS wrapper, the AddControl() calls are done after the setPlayConfigDetails() call, then the plug-in knows its i/o channel count before declaring parameters.

But in the AAX wrapper, (in EffectInit()), the parameters are declared before the setPlayConfigDetails() (in preparePlugin()).

Fixed by calling preparePlugin() before addBypassParameter() and addAudioProcessorParameters() rather than after these calls.

SampleRate caching
The samplerate does not change during the lifetime of a plug-in instance (from doc). Therefore we can create a member for that: AAX_CSampleRate JuceAAXProcessor::sampleRate and initialize it at the beginning of EffectInit: check (Controller()->GetSampleRate (&sampleRate));. Then, the getSampleRate() method can disappear and its callers can use the new sampleRate member.

I had an issue with the buffer size: indeed with AAX no buffersize can be predicted, however I wanted the plug-in to be informed of the buffer size through prepareToPlay, instead of being stuck with 0 and having to manually call it inside of the processBlock & processBlockBypassed. So, I added an int JuceAAXProcessor::mBufferSize member, initialized in EffectInit() to its max possible value (1024), and altered in process(), before the processBlock & processBlockBypassed calls:

if (mBufferSize != bufferSize)
mBufferSize = bufferSize;
pluginInstance->prepareToPlay(sampleRate, mBufferSize);

const ScopedLocked sl (pluginInstance->getCallbackLock());

if (bypassed)
    pluginInstance->processBlockBypassed (buffer, midiBuffer);

(and change bufferSize to mBufferSize in the setPlayConfigDetails() call parameters).

Current position retrieving

I also had issues with the GetTimelineSelectionStartPosition() call, which does not always return a usable value, and even asserts because of the check (probably because of the threading scope of the caller of getCurrentPosition()). It can be fixed by having a fallback call:

if (! info.isPlaying) { AAX_Result ret = transport.GetTimelineSelectionStartPosition (&info.timeInSamples); if (ret != AAX_SUCCESS) check (transport.GetCurrentNativeSampleLocation (&info.timeInSamples)); } else check (transport.GetCurrentNativeSampleLocation (&info.timeInSamples));

Non-automatable parameters

Why are non-automatable parameters not declared to the host?


Thanks very much! And those all seem like pretty solid suggestions - I’ve merged a lot of these changes in, would appreciate you and any other AAX people trying them out!


Great, thanks Jules! I just merged the changes and tested it, it’s working.

The first point ( channels <= 8 ) is also applicable to RTAS, by the way.

About the last point “Non-automatable parameters”: in the current juce_AAX_Wrapper.cpp implementation, it’s

if (audioProcessor.isParameterAutomatable (parameterIndex)) { [declare the parameter to the host] }

I suggest to remove the if condition and plainly do:

bool automatable = audioProcessor.isParameterAutomatable (parameterIndex)); AAX_IParameter* parameter = new AAX_CParameter<float>([no change there, but the last parameter, instead of true:] automatable);

This way, non-automatable parameters will be declared to the host, in addition to the automatable parameters. It may look an unessential change, but it’s required for control surfaces.


Yes, thanks, I’ll sort that out. TBH I can’t remember why only automatable params were included…


Jules, I owe you some other stuff in the AAX wrapper regarding EuControl. It’s working well, but I’m a little too busy in the getting-it-out-the door department at the moment. Late June/early July and I’ll get it to you.


Thanks Jules for the update of the AAX wrapper.

About EUCON: I did post everything needed in some other posts of the forum, at least for RTAS (tested and running). I also did that for AAX (that was an easy port, but not yet fully tested). Want that (again)?


[quote=“fbecker”]Thanks Jules for the update of the AAX wrapper.

About EUCON: I did post everything needed in some other posts of the forum, at least for RTAS (tested and running). I also did that for AAX (that was an easy port, but not yet fully tested). Want that (again)?[/quote]

Don’t know what you did, but I’ve got working code here as well. I’ve tested with RTAS, AAX and VST with an Avid Artist Mix (it also works with AU, but the AU API isn’t really smart enough to do a good job with it). EuCon reaches farther than just the wrapper, if you want to support it completely. A solid implementation needs variable parameter name sizes as well as variable display sizes. I’ve found a way to add just a couple of extra virtual calls to Juce in such a way that someone with no interest in EuCon doesn’t have to mess with it. PM me if you’d like to compare notes.


Isn’t that a bit ugly and dangerous to call prepareToPlay inside the process callback ? I thought prepareToPlay was the place where heavy initialisation occurs, and was really not intended to be called inside a realtime callback


The only thing I use prepareToPlay is for the role you describe. Never, never in realtime. Didn’t mean to give a contrary impression.