Presets/programs in AUs


#1

Hi,

I have currently 4 programs in my plug which all work fine under Windows/VST, i.e. get/setprogram() and getprogramname() are implemented correctly. However, under Garageband in 10.4 the corresponding AU component doesn’t show any programs. What else do I have to declare to make proper AU presets/programs?


#2

ah, I don’t think that’s actually implemented yet under AU… I’ll get round to it eventually, or if you want to hurry things up, you could have a go yourself!


#3

Okay, I might try it… although I can’t promise success!


#4

So that’s what I’ve come up with so far, basically following the TremoloUnit example in the Apple developer’s guide:

	ComponentResult GetPresets (CFArrayRef *outData) const
	{	
		if (outData == NULL) 
		{
			return noErr;
		}
		
		int kNumberPresets = juceFilter->getNumPrograms();
		AUPreset kPresets[kNumberPresets];
		String programName;
		
		CFMutableArrayRef presetsArray = CFArrayCreateMutable(NULL,kNumberPresets,NULL);
		for (int i=0;i<kNumberPresets;i++) 
		{
			kPresets[i].presetNumber = i;
			programName = juceFilter->getProgramName(i);
			kPresets[i].presetName = PlatformUtilities::juceStringToCFString (programName);
			CFArrayAppendValue(presetsArray,&kPresets[i]);
		}
		*outData = (CFArrayRef) presetsArray;
		return noErr;
	}
	
	OSStatus NewFactoryPresetSet( const AUPreset &inNewFactoryPreset)
	{
		int kNumberPresets = juceFilter->getNumPrograms();
		SInt32 chosenPresetNumber = (int) inNewFactoryPreset.presetNumber;
		AUPreset chosenPreset;
		String programName;
		
		if (chosenPresetNumber<kNumberPresets) 
		{
			chosenPreset.presetNumber = chosenPresetNumber;
			programName = juceFilter->getProgramName(chosenPresetNumber);
			chosenPreset.presetName = PlatformUtilities::juceStringToCFString (programName); 
			juceFilter->setCurrentProgram(chosenPresetNumber);
			SetAFactoryPresetAsCurrent(chosenPreset);
			return noErr;
		}
		else return kAudioUnitErr_InvalidProperty;
	} 

While in getPreset() everything seems to work fine, NewFactoryPresetSet is called with inNewFactoryPreset.presetNumber=104345483 or something like that. Consequently, the routine returns kAudioUnitErr_InvalidProperty, and then the whole thing crashes. Anything obvious that I might have forgotten?


#5

Alright, the kPresets array in getPreset() has to be global/static. So this seems to work:

  • add the following two variables to JuceAU:

static AUPreset *kPresets; static bool kPresetsNotAlloc;

  • initiate them by writing AUPreset* JuceAU::kPresets=NULL; bool JuceAU::kPresetsNotAlloc=true; somewhere into juce_AudioUnitWrapper.cpp. Then, the two preset routines look like this:

[code]ComponentResult GetPresets (CFArrayRef *outData) const
{
if (outData == NULL)
{
return noErr;
}

	int kNumberPresets = juceFilter->getNumPrograms();
	if (kPresetsNotAlloc)
	{
		kPresets = new AUPreset[kNumberPresets];
		kPresetsNotAlloc = false;
	}
	
	String programName;
	
	CFMutableArrayRef presetsArray = CFArrayCreateMutable(NULL,kNumberPresets,NULL);
	for (int i=0;i<kNumberPresets;i++) 
	{
		kPresets[i].presetNumber = i;
		programName = juceFilter->getProgramName(i);
		kPresets[i].presetName = PlatformUtilities::juceStringToCFString (programName);
		CFArrayAppendValue(presetsArray,&kPresets[i]);
	}
	*outData = (CFArrayRef) presetsArray;
	return noErr;
}

OSStatus NewFactoryPresetSet( const AUPreset &inNewFactoryPreset)
{
	int kNumberPresets = juceFilter->getNumPrograms();
	SInt32 chosenPresetNumber = (int) inNewFactoryPreset.presetNumber;
	AUPreset chosenPreset;
	String programName;
	
	if (chosenPresetNumber<kNumberPresets) 
	{
		chosenPreset.presetNumber = chosenPresetNumber;
		programName = juceFilter->getProgramName(chosenPresetNumber);
		chosenPreset.presetName = PlatformUtilities::juceStringToCFString (programName); 
		juceFilter->setCurrentProgram(chosenPresetNumber);
		SetAFactoryPresetAsCurrent(chosenPreset);
		return noErr;
	}
	
	else return kAudioUnitErr_InvalidProperty;
} 

[/code]

I haven’t tested it extensively, but so far things seem fine…

Edit: Don’t forget to delete the kPresets array in the JuceAU destructor…


#6

Ok, thanks. Obviously making the presets global is no good when you’ve got multiple instances of a plugin, but try this re-factored version using a MemoryBlock member variable to store the array (I’ve not even checked if this compiles yet…):

[code] MemoryBlock presetsArray;

ComponentResult GetPresets (CFArrayRef* outData) const
{
    if (outData != 0)
    {
        const int numPrograms = juceFilter->getNumPrograms();
        presetsArray.ensureSize (sizeof (AUPreset) * numPrograms, true);
        AUPreset* const presets = (AUPreset*) presetsArray.getData();

        CFMutableArrayRef presetsArray = CFArrayCreateMutable (0, numPrograms, 0);

        for (int i = 0; i < numPrograms; ++i)
        {
            presets[i].presetNumber = i;
            presets[i].presetName = PlatformUtilities::juceStringToCFString (juceFilter->getProgramName (i));
            
            CFArrayAppendValue (presetsArray, presets + i);
        }

        *outData = (CFArrayRef) presetsArray;
    }

    return noErr;
}

OSStatus NewFactoryPresetSet (const AUPreset& inNewFactoryPreset)
{
    const int numPrograms = juceFilter->getNumPrograms();
    const SInt32 chosenPresetNumber = (int) inNewFactoryPreset.presetNumber;

    if (chosenPresetNumber >= numPrograms)
        return kAudioUnitErr_InvalidProperty;

    AUPreset chosenPreset;
    chosenPreset.presetNumber = chosenPresetNumber;
    chosenPreset.presetName = PlatformUtilities::juceStringToCFString (juceFilter->getProgramName (chosenPresetNumber));

    juceFilter->setCurrentProgram (chosenPresetNumber);
    SetAFactoryPresetAsCurrent (chosenPreset);

    return noErr;
} [/code]

#7

Hmmm… works fine with multiple plugins. I don’t get it: why should this be bad? It’s the same set of presets for every instance, isn’t it?


#8

Maybe in your plugin it is, but there’s no reason why that should be the case for everyone. Besides, there’s an obvious bug if one plugin is deleted and deletes the statics while another is using them. In general, statics in plugins are best avoided.


#9

I see… thanks! Everything compiles and works fine…


#10

Great, I’ll check it in.


#11

does not work here:

presetsArray.ensureSize (sizeof (AUPreset) * numPrograms, true);

error: passing 'const juce::MemoryBlock' as 'this' argument of 'void juce::MemoryBlock::ensureSize(int, bool)' discards qualifiers

cause GetPresets is const.

i also don’t understand why presetsArray is defined a second time


            CFMutableArrayRef presetsArray = CFArrayCreateMutable (0, numPrograms, 0); 


#12

ok, better make that MemoryBlock mutable…


#13