From 3c0d634b4fa0876d78e7dc156db4b72f51945ddf Mon Sep 17 00:00:00 2001 From: Tom Poole Date: Thu, 1 Feb 2018 12:05:41 +0000 Subject: [PATCH] AUv3: Improved the host-provided parameter views --- .../AU/juce_AU_Wrapper.mm | 18 ++-- .../AU/juce_AUv3_Wrapper.mm | 102 ++++++++++++++++-- 2 files changed, 103 insertions(+), 17 deletions(-) diff --git a/modules/juce_audio_plugin_client/AU/juce_AU_Wrapper.mm b/modules/juce_audio_plugin_client/AU/juce_AU_Wrapper.mm index 0e4eb016f8..b8031be92c 100644 --- a/modules/juce_audio_plugin_client/AU/juce_AU_Wrapper.mm +++ b/modules/juce_audio_plugin_client/AU/juce_AU_Wrapper.mm @@ -525,11 +525,11 @@ public: { if (juceFilter != nullptr) { - const int paramID = getJuceIndexForAUParameterID (pv->inParamID); + const int i = getJuceIndexForAUParameterID (pv->inParamID); const String text (String::fromCFString (pv->inString)); - if (AudioProcessorParameter* param = juceFilter->getParameters() [paramID]) - pv->outValue = param->getValueForText (text) * getMaximumParameterValue (paramID); + if (AudioProcessorParameter* param = juceFilter->getParameters()[i]) + pv->outValue = param->getValueForText (text) * getMaximumParameterValue (i); else pv->outValue = text.getFloatValue(); @@ -545,12 +545,12 @@ public: { if (juceFilter != nullptr) { - const int paramID = getJuceIndexForAUParameterID (pv->inParamID); + const int i = getJuceIndexForAUParameterID (pv->inParamID); const float value = (float) *(pv->inValue); String text; - if (AudioProcessorParameter* param = juceFilter->getParameters() [paramID]) - text = param->getText (value / getMaximumParameterValue (paramID), 0); + if (AudioProcessorParameter* param = juceFilter->getParameters()[i]) + text = param->getText (value / getMaximumParameterValue (i), 0); else text = String (value); @@ -776,7 +776,7 @@ public: return (UInt32) layouts.size(); } - OSStatus SetAudioChannelLayout(AudioUnitScope scope, AudioUnitElement element, const AudioChannelLayout* inLayout) override + OSStatus SetAudioChannelLayout (AudioUnitScope scope, AudioUnitElement element, const AudioChannelLayout* inLayout) override { bool isInput; int busNr; @@ -877,8 +877,8 @@ public: else { #if ! JUCE_FORCE_LEGACY_PARAMETER_AUTOMATION_TYPE - outParameterInfo.unit = isParameterDiscrete ? kAudioUnitParameterUnit_Indexed - : kAudioUnitParameterUnit_Generic; + if (isParameterDiscrete) + outParameterInfo.unit = kAudioUnitParameterUnit_Indexed; #endif } diff --git a/modules/juce_audio_plugin_client/AU/juce_AUv3_Wrapper.mm b/modules/juce_audio_plugin_client/AU/juce_AUv3_Wrapper.mm index b5b9640958..1a36bca77e 100644 --- a/modules/juce_audio_plugin_client/AU/juce_AUv3_Wrapper.mm +++ b/modules/juce_audio_plugin_client/AU/juce_AUv3_Wrapper.mm @@ -1082,13 +1082,22 @@ private: busses: array]; } + // When parameters are discrete we need to use integer values. + float getMaximumParameterValue (int parameterIndex) + { + #if JUCE_FORCE_LEGACY_PARAMETER_AUTOMATION_TYPE + ignoreUnused (parameterIndex); + return 1.0f; + #else + auto& processor = getAudioProcessor(); + return processor.isParameterDiscrete (parameterIndex) ? (float) (processor.getParameterNumSteps (parameterIndex) - 1) : 1.0f; + #endif + } + void addParameters() { ScopedPointer> params = [[NSMutableArray alloc] init]; - paramObserver = CreateObjCBlock (this, &JuceAudioUnitv3::valueChangedFromHost); - paramProvider = CreateObjCBlock (this, &JuceAudioUnitv3::getValue); - overviewParams = [[NSMutableArray alloc] init]; auto& processor = getAudioProcessor(); @@ -1118,9 +1127,17 @@ private: if (name.isEmpty() || ! processor.isParameterAutomatable (idx)) flags |= kAudioUnitParameterFlag_NonRealTime; + const bool isParameterDiscrete = processor.isParameterDiscrete (idx); + + if (! isParameterDiscrete) + flags |= kAudioUnitParameterFlag_CanRamp; + if (processor.isMetaParameter (idx)) flags |= kAudioUnitParameterFlag_IsGlobalMeta; + auto deleter = [](NSMutableArray* arr) { [arr release]; }; + std::unique_ptr valueStrings (nullptr, deleter); + // is this a meter? if (((processor.getParameterCategory (idx) & 0xffff0000) >> 16) == 2) { @@ -1128,13 +1145,36 @@ private: flags |= kAudioUnitParameterFlag_MeterReadOnly | kAudioUnitParameterFlag_DisplayLogarithmic; unit = kAudioUnitParameterUnit_LinearGain; } + else + { + #if ! JUCE_FORCE_LEGACY_PARAMETER_AUTOMATION_TYPE + if (auto* param = processor.getParameters()[idx]) + { + if (param->isDiscrete()) + { + unit = kAudioUnitParameterUnit_Indexed; + auto maxValue = getMaximumParameterValue (idx); + auto numSteps = param->getNumSteps(); + + // Some hosts can't handle the huge numbers of discrete parameter values created when + // using the default number of steps. + jassert (numSteps != AudioProcessor::getDefaultNumParameterSteps()); + + valueStrings.reset ([NSMutableArray new]); + + for (int i = 0; i < numSteps; ++i) + [valueStrings.get() addObject: juceStringToNS (param->getText ((float) i / maxValue, 0))]; + } + } + #endif + } #if JUCE_FORCE_USE_LEGACY_PARAM_IDS AUParameterAddress address = static_cast (idx); #else AUParameterAddress address = generateAUParameterAddressForIndex (idx); - // Consider yourself very unlucky if you hit this assertion. The hash code of your + // Consider yourself very unlucky if you hit this assertion. The hash codes of your // parameter ids are not unique. jassert (! paramMap.contains (static_cast (address))); @@ -1148,13 +1188,15 @@ private: name: juceStringToNS (name) address: address min: 0.0f - max: 1.0f + max: getMaximumParameterValue (idx) unit: unit unitName: nullptr flags: flags - valueStrings: nullptr + valueStrings: valueStrings.get() dependentParameters: nullptr] retain]; + [param.get() setValue: processor.getParameterDefaultValue (idx)]; + [params addObject: param]; [overviewParams addObject: [NSNumber numberWithUnsignedLongLong:address]]; } @@ -1162,8 +1204,15 @@ private: // create methods in AUParameterTree return unretained objects (!) -> see Apple header AUAudioUnitImplementation.h paramTree = [[AUParameterTree createTreeWithChildren: params] retain]; + paramObserver = CreateObjCBlock (this, &JuceAudioUnitv3::valueChangedFromHost); + paramProvider = CreateObjCBlock (this, &JuceAudioUnitv3::getValue); + stringFromValueProvider = CreateObjCBlock (this, &JuceAudioUnitv3::stringFromValue); + valueFromStringProvider = CreateObjCBlock (this, &JuceAudioUnitv3::valueFromString); + [paramTree setImplementorValueObserver: paramObserver]; [paramTree setImplementorValueProvider: paramProvider]; + [paramTree setImplementorStringFromValueCallback: stringFromValueProvider]; + [paramTree setImplementorValueFromStringCallback: valueFromStringProvider]; if (processor.hasEditor()) { @@ -1375,7 +1424,7 @@ private: auto& processor = getAudioProcessor(); if (isPositiveAndBelow (idx, processor.getNumParameters())) - processor.setParameter (idx, value); + processor.setParameter (idx, value / getMaximumParameterValue (idx)); } } @@ -1387,7 +1436,7 @@ private: auto& processor = getAudioProcessor(); if (isPositiveAndBelow (idx, processor.getNumParameters())) - return processor.getParameter (idx); + return processor.getParameter (idx) * getMaximumParameterValue (idx); } return 0; @@ -1398,6 +1447,41 @@ private: // this will have already been handled by valueChangedFromHost } + NSString* stringFromValue (AUParameter* param, const AUValue* value) + { + String text; + + if (param != nullptr && value != nullptr) + { + const int idx = getJuceParameterIndexForAUAddress ([param address]); + auto& processor = getAudioProcessor(); + + if (auto* p = processor.getParameters()[idx]) + text = p->getText (*value / getMaximumParameterValue (idx), 0); + else + text = String (*value); + } + + return juceStringToNS (text); + } + + AUValue valueFromString (AUParameter* param, NSString* str) + { + if (param != nullptr && str != nullptr) + { + const int idx = getJuceParameterIndexForAUAddress ([param address]); + auto& processor = getAudioProcessor(); + const String text (nsStringToJuce (str)); + + if (auto* p = processor.getParameters()[idx]) + return p->getValueForText (text) * getMaximumParameterValue (idx); + else + return text.getFloatValue(); + } + + return 0; + } + //============================================================================== #if JUCE_FORCE_USE_LEGACY_PARAM_IDS inline AUParameterAddress getAUParameterAddressForIndex (int paramIndex) const noexcept { return static_cast (paramIndex); } @@ -1443,6 +1527,8 @@ private: ObjCBlock paramObserver; ObjCBlock paramProvider; + ObjCBlock stringFromValueProvider; + ObjCBlock valueFromStringProvider; #if ! JUCE_FORCE_USE_LEGACY_PARAM_IDS bool usingManagedParameter;