Skip to content

Commit

Permalink
Added plug-in parameter groups
Browse files Browse the repository at this point in the history
  • Loading branch information
tpoole committed Sep 13, 2018
1 parent af041a0 commit 7e1db1a
Show file tree
Hide file tree
Showing 11 changed files with 1,090 additions and 360 deletions.
36 changes: 36 additions & 0 deletions modules/juce_audio_plugin_client/AU/juce_AU_Wrapper.mm
Expand Up @@ -474,6 +474,31 @@ ComponentResult GetProperty (AudioUnitPropertyID inID,
{
switch (inID)
{
case kAudioUnitProperty_ParameterClumpName:

if (auto* clumpNameInfo = (AudioUnitParameterNameInfo*) outData)
{
if (juceFilter != nullptr)
{
auto clumpIndex = clumpNameInfo->inID - 1;
const auto* group = parameterGroups[(int) clumpIndex];
auto name = group->getName();

while (group->getParent() != &juceFilter->getParameterTree())
{
group = group->getParent();
name = group->getName() + group->getSeparator() + name;
}

clumpNameInfo->outName = name.toCFString();
return noErr;
}
}

// Failed to find a group corresponding to the clump ID.
jassertfalse;
break;

case juceFilterObjectPropertyID:
((void**) outData)[0] = (void*) static_cast<AudioProcessor*> (juceFilter.get());
((void**) outData)[1] = (void*) this;
Expand Down Expand Up @@ -879,6 +904,14 @@ ComponentResult GetParameterInfo (AudioUnitScope inScope,
if (param->isMetaParameter())
outParameterInfo.flags |= kAudioUnitParameterFlag_IsGlobalMeta;

auto parameterGroupHierarchy = juceFilter->getParameterTree().getGroupsForParameter (param);

if (! parameterGroupHierarchy.isEmpty())
{
outParameterInfo.flags |= kAudioUnitParameterFlag_HasClump;
outParameterInfo.clumpID = (UInt32) parameterGroups.indexOf (parameterGroupHierarchy.getLast()) + 1;
}

// Is this a meter?
if (((param->getCategory() & 0xffff0000) >> 16) == 2)
{
Expand Down Expand Up @@ -1687,6 +1720,7 @@ static BOOL mouseDownCanMoveWindow (id, SEL)
LegacyAudioParametersWrapper juceParameters;
HashMap<int32, AudioProcessorParameter*> paramMap;
Array<AudioUnitParameterID> auParamIDs;
Array<const AudioProcessorParameterGroup*> parameterGroups;

//==============================================================================
AudioUnitEvent auEvent;
Expand Down Expand Up @@ -1839,6 +1873,8 @@ OSStatus elementToBusIdx (AudioUnitScope scope, AudioUnitElement element, bool&
//==============================================================================
void addParameters()
{
parameterGroups = juceFilter->getParameterTree().getSubgroups (true);

juceParameters.update (*juceFilter, forceUseLegacyParamIDs);
const int numParams = juceParameters.getNumParameters();

Expand Down
229 changes: 146 additions & 83 deletions modules/juce_audio_plugin_client/AU/juce_AUv3_Wrapper.mm
Expand Up @@ -1157,107 +1157,175 @@ float getMaximumParameterValue (AudioProcessorParameter* juceParam)
#endif
}

void addParameters()
std::unique_ptr<AUParameter, NSObjectDeleter> createParameter (AudioProcessorParameter* parameter)
{
std::unique_ptr<NSMutableArray<AUParameterNode*>, NSObjectDeleter> params ([[NSMutableArray<AUParameterNode*> alloc] init]);
const String name (parameter->getName (512));

overviewParams.reset ([[NSMutableArray<NSNumber*> alloc] init]);
AudioUnitParameterUnit unit = kAudioUnitParameterUnit_Generic;
AudioUnitParameterOptions flags = (UInt32) (kAudioUnitParameterFlag_IsWritable
| kAudioUnitParameterFlag_IsReadable
| kAudioUnitParameterFlag_HasCFNameString
| kAudioUnitParameterFlag_ValuesHaveStrings);

auto& processor = getAudioProcessor();
juceParameters.update (processor, forceLegacyParamIDs);
if (! forceLegacyParamIDs)
flags |= (UInt32) kAudioUnitParameterFlag_IsHighResolution;

const int n = juceParameters.getNumParameters();
// Set whether the param is automatable (unnamed parameters aren't allowed to be automated).
if (name.isEmpty() || ! parameter->isAutomatable())
flags |= kAudioUnitParameterFlag_NonRealTime;

for (int idx = 0; idx < n; ++idx)
{
auto* juceParam = juceParameters.getParamForIndex (idx);
const bool isParameterDiscrete = parameter->isDiscrete();

if (! isParameterDiscrete)
flags |= kAudioUnitParameterFlag_CanRamp;

const String identifier (idx);
const String name = juceParam->getName (512);
if (parameter->isMetaParameter())
flags |= kAudioUnitParameterFlag_IsGlobalMeta;

AudioUnitParameterUnit unit = kAudioUnitParameterUnit_Generic;
AudioUnitParameterOptions flags = (UInt32) (kAudioUnitParameterFlag_IsWritable
| kAudioUnitParameterFlag_IsReadable
| kAudioUnitParameterFlag_HasCFNameString
| kAudioUnitParameterFlag_ValuesHaveStrings);
std::unique_ptr<NSMutableArray, NSObjectDeleter> valueStrings;

// Is this a meter?
if (((parameter->getCategory() & 0xffff0000) >> 16) == 2)
{
flags &= ~kAudioUnitParameterFlag_IsWritable;
flags |= kAudioUnitParameterFlag_MeterReadOnly | kAudioUnitParameterFlag_DisplayLogarithmic;
unit = kAudioUnitParameterUnit_LinearGain;
}
else
{
if (! forceLegacyParamIDs)
flags |= (UInt32) kAudioUnitParameterFlag_IsHighResolution;
{
if (parameter->isDiscrete())
{
unit = parameter->isBoolean() ? kAudioUnitParameterUnit_Boolean : kAudioUnitParameterUnit_Indexed;
auto maxValue = getMaximumParameterValue (parameter);
auto numSteps = parameter->getNumSteps();

// set whether the param is automatable (unnamed parameters aren't allowed to be automated)
if (name.isEmpty() || ! juceParam->isAutomatable())
flags |= kAudioUnitParameterFlag_NonRealTime;
// Some hosts can't handle the huge numbers of discrete parameter values created when
// using the default number of steps.
jassert (numSteps != AudioProcessor::getDefaultNumParameterSteps());

const bool isParameterDiscrete = juceParam->isDiscrete();
valueStrings.reset ([NSMutableArray new]);

if (! isParameterDiscrete)
flags |= kAudioUnitParameterFlag_CanRamp;
for (int i = 0; i < numSteps; ++i)
[valueStrings.get() addObject: juceStringToNS (parameter->getText ((float) i / maxValue, 0))];
}
}
}

if (juceParam->isMetaParameter())
flags |= kAudioUnitParameterFlag_IsGlobalMeta;
AUParameterAddress address = generateAUParameterAddress (parameter);

std::unique_ptr<NSMutableArray, NSObjectDeleter> valueStrings;
#if ! JUCE_FORCE_USE_LEGACY_PARAM_IDS
// If you hit this assertion then you have either put a parameter in two groups or you are
// very unlucky and the hash codes of your parameter ids are not unique.
jassert (! paramMap.contains (static_cast<int64> (address)));

// is this a meter?
if (((juceParam->getCategory() & 0xffff0000) >> 16) == 2)
{
flags &= ~kAudioUnitParameterFlag_IsWritable;
flags |= kAudioUnitParameterFlag_MeterReadOnly | kAudioUnitParameterFlag_DisplayLogarithmic;
unit = kAudioUnitParameterUnit_LinearGain;
}
paramAddresses.add (address);
paramMap.set (static_cast<int64> (address), parameter->getParameterIndex());
#endif

auto getParameterIdentifier = [parameter]
{
if (auto* paramWithID = dynamic_cast<AudioProcessorParameterWithID*> (parameter))
return paramWithID->paramID;

// This could clash if any groups have been given integer IDs!
return String (parameter->getParameterIndex());
};

std::unique_ptr<AUParameter, NSObjectDeleter> param;

@try
{
// Create methods in AUParameterTree return unretained objects (!) -> see Apple header AUAudioUnitImplementation.h
param.reset([[AUParameterTree createParameterWithIdentifier: juceStringToNS (getParameterIdentifier())
name: juceStringToNS (name)
address: address
min: 0.0f
max: getMaximumParameterValue (parameter)
unit: unit
unitName: nullptr
flags: flags
valueStrings: valueStrings.get()
dependentParameters: nullptr]
retain]);
}

@catch (NSException* exception)
{
// Do you have duplicate identifiers in any of your groups or parameters,
// or do your identifiers have unusual characters in them?
jassertfalse;
}

[param.get() setValue: parameter->getDefaultValue()];

[overviewParams.get() addObject: [NSNumber numberWithUnsignedLongLong: address]];

return param;
}

std::unique_ptr<AUParameterGroup, NSObjectDeleter> createParameterGroup (AudioProcessorParameterGroup* group)
{
std::unique_ptr<NSMutableArray<AUParameterNode*>, NSObjectDeleter> children ([NSMutableArray<AUParameterNode*> new]);

for (auto* node : *group)
{
if (auto* childGroup = node->getGroup())
[children.get() addObject: createParameterGroup (childGroup).get()];
else
{
if (! forceLegacyParamIDs)
{
if (juceParam->isDiscrete())
{
unit = juceParam->isBoolean() ? kAudioUnitParameterUnit_Boolean : kAudioUnitParameterUnit_Indexed;
auto maxValue = getMaximumParameterValue (juceParam);
auto numSteps = juceParam->getNumSteps();
[children.get() addObject: createParameter (node->getParameter()).get()];
}

// Some hosts can't handle the huge numbers of discrete parameter values created when
// using the default number of steps.
jassert (numSteps != AudioProcessor::getDefaultNumParameterSteps());
std::unique_ptr<AUParameterGroup, NSObjectDeleter> result;

valueStrings.reset ([NSMutableArray new]);
@try
{
// Create methods in AUParameterTree return unretained objects (!) -> see Apple header AUAudioUnitImplementation.h
result.reset ([[AUParameterTree createGroupWithIdentifier: juceStringToNS (group->getID())
name: juceStringToNS (group->getName())
children: children.get()]
retain]);
}

for (int i = 0; i < numSteps; ++i)
[valueStrings.get() addObject: juceStringToNS (juceParam->getText ((float) i / maxValue, 0))];
}
}
}
@catch (NSException* exception)
{
// Do you have duplicate identifiers in any of your groups or parameters,
// or do your identifiers have unusual characters in them?
jassertfalse;
}

AUParameterAddress address = generateAUParameterAddress (juceParam);
return result;
}

#if ! JUCE_FORCE_USE_LEGACY_PARAM_IDS
// Consider yourself very unlucky if you hit this assertion. The hash codes of your
// parameter ids are not unique.
jassert (! paramMap.contains (static_cast<int64> (address)));
void addParameters()
{
auto& processor = getAudioProcessor();
juceParameters.update (processor, forceLegacyParamIDs);

paramAddresses.add (address);
paramMap.set (static_cast<int64> (address), idx);
#endif
// This is updated when we build the tree.
overviewParams.reset ([NSMutableArray<NSNumber*> new]);

std::unique_ptr<NSMutableArray<AUParameterNode*>, NSObjectDeleter> topLevelNodes ([NSMutableArray<AUParameterNode*> new]);

for (auto* node : processor.getParameterTree())
if (auto* childGroup = node->getGroup())
[topLevelNodes.get() addObject: createParameterGroup (childGroup).get()];
else
[topLevelNodes.get() addObject: createParameter (node->getParameter()).get()];

// create methods in AUParameterTree return unretained objects (!) -> see Apple header AUAudioUnitImplementation.h
std::unique_ptr<AUParameter, NSObjectDeleter> param ([[AUParameterTree createParameterWithIdentifier: juceStringToNS (identifier)
name: juceStringToNS (name)
address: address
min: 0.0f
max: getMaximumParameterValue (juceParam)
unit: unit
unitName: nullptr
flags: flags
valueStrings: valueStrings.get()
dependentParameters: nullptr] retain]);

[param.get() setValue: juceParam->getDefaultValue()];

[params.get() addObject: param.get()];
[overviewParams.get() addObject: [NSNumber numberWithUnsignedLongLong:address]];
@try
{
// Create methods in AUParameterTree return unretained objects (!) -> see Apple header AUAudioUnitImplementation.h
paramTree.reset ([[AUParameterTree createTreeWithChildren: topLevelNodes.get()] retain]);
}

// create methods in AUParameterTree return unretained objects (!) -> see Apple header AUAudioUnitImplementation.h
paramTree.reset ([[AUParameterTree createTreeWithChildren: params.get()] retain]);
@catch (NSException* exception)
{
// Do you have duplicate identifiers in any of your groups or parameters,
// or do your identifiers have unusual characters in them?
jassertfalse;
}

paramObserver = CreateObjCBlock (this, &JuceAudioUnitv3::valueChangedFromHost);
paramProvider = CreateObjCBlock (this, &JuceAudioUnitv3::getValue);
Expand Down Expand Up @@ -1582,13 +1650,8 @@ AUParameterAddress generateAUParameterAddress (AudioProcessorParameter* param) c
{
const String& juceParamID = LegacyAudioParameter::getParamID (param, forceLegacyParamIDs);

#if JUCE_FORCE_USE_LEGACY_PARAM_IDS
auto result = juceParamID.getIntValue();
#else
auto result = juceParamID.hashCode64();
#endif

return static_cast<AUParameterAddress> (result);
return static_cast<AUParameterAddress> (forceLegacyParamIDs ? juceParamID.getIntValue()
: juceParamID.hashCode64());
}

AudioProcessorParameter* getJuceParameterForAUAddress (AUParameterAddress address) const noexcept
Expand Down

0 comments on commit 7e1db1a

Please sign in to comment.