diff --git a/extras/AudioPluginHost/Source/GraphEditorPanel.cpp b/extras/AudioPluginHost/Source/GraphEditorPanel.cpp index 40cf310bc7..35edbbf222 100644 --- a/extras/AudioPluginHost/Source/GraphEditorPanel.cpp +++ b/extras/AudioPluginHost/Source/GraphEditorPanel.cpp @@ -307,6 +307,7 @@ struct GraphEditorPanel::FilterComponent : public Component case 12: showWindow (PluginWindow::Type::generic); break; case 20: showWindow (PluginWindow::Type::audioIO); break; case 21: testStateSaveLoad(); break; + default: break; } } diff --git a/modules/juce_audio_plugin_client/AAX/juce_AAX_Wrapper.cpp b/modules/juce_audio_plugin_client/AAX/juce_AAX_Wrapper.cpp index fae56368dc..077623b720 100644 --- a/modules/juce_audio_plugin_client/AAX/juce_AAX_Wrapper.cpp +++ b/modules/juce_audio_plugin_client/AAX/juce_AAX_Wrapper.cpp @@ -34,6 +34,8 @@ #include "../utility/juce_WindowsHooks.h" #include "../utility/juce_FakeMouseMoveGenerator.h" +#include "../../juce_audio_processors/format_types/juce_LegacyAudioParameter.cpp" + #ifdef __clang__ #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wnon-virtual-dtor" @@ -635,6 +637,7 @@ namespace AAXClasses AAX_Result Uninitialize() override { cancelPendingUpdate(); + juceParameters.clear(); if (isPrepared && pluginInstance != nullptr) { @@ -724,11 +727,11 @@ namespace AAXClasses // * The preset is loaded in PT 10 using the AAX version. // * The session is then saved, and closed. // * The saved session is loaded, but acting as if the preset was never loaded. - auto numParameters = pluginInstance->getNumParameters(); + auto numParameters = juceParameters.getNumParameters(); for (int i = 0; i < numParameters; ++i) if (auto paramID = getAAXParamIDFromJuceIndex(i)) - SetParameterNormalizedValue (paramID, (double) pluginInstance->getParameter(i)); + SetParameterNormalizedValue (paramID, juceParameters.getParamForIndex (i)->getValue()); return AAX_SUCCESS; } @@ -782,22 +785,20 @@ namespace AAXClasses return AAX_SUCCESS; } - void setAudioProcessorParameter (int index, float value) + void setAudioProcessorParameter (AAX_CParamID paramID, double value) { - if (auto* param = pluginInstance->getParameters()[index]) + if (auto* param = getParameterFromID (paramID)) { - if (value != param->getValue()) + auto newValue = static_cast (value); + + if (newValue != param->getValue()) { - param->setValue (value); + param->setValue (newValue); inParameterChangedCallback = true; - param->sendValueChangedMessageToListeners (value); + param->sendValueChangedMessageToListeners (newValue); } } - else - { - pluginInstance->setParameter (index, value); - } } AAX_Result UpdateParameterNormalizedValue (AAX_CParamID paramID, double value, AAX_EUpdateSource source) override @@ -805,7 +806,7 @@ namespace AAXClasses auto result = AAX_CEffectParameters::UpdateParameterNormalizedValue (paramID, value, source); if (! isBypassParam (paramID)) - setAudioProcessorParameter (getParamIndexFromID (paramID), (float) value); + setAudioProcessorParameter (paramID, value); return result; } @@ -818,10 +819,13 @@ namespace AAXClasses return AAX_SUCCESS; } - if (AudioProcessorParameter* param = pluginInstance->getParameters() [getParamIndexFromID (paramID)]) + if (auto* param = getParameterFromID (paramID)) { - *result = param->getValueForText (text.Get()); - return AAX_SUCCESS; + if (! LegacyAudioParameter::isLegacy (param)) + { + *result = param->getValueForText (text.Get()); + return AAX_SUCCESS; + } } return AAX_CEffectParameters::GetParameterValueFromString (paramID, result, text); @@ -835,15 +839,8 @@ namespace AAXClasses } else { - auto paramIndex = getParamIndexFromID (paramID); - juce::String text; - - if (auto* param = pluginInstance->getParameters() [paramIndex]) - text = param->getText ((float) value, maxLen); - else - text = pluginInstance->getParameterText (paramIndex, maxLen); - - result->Set (text.toRawUTF8()); + if (auto* param = getParameterFromID (paramID)) + result->Set (param->getText ((float) value, maxLen).toRawUTF8()); } return AAX_SUCCESS; @@ -852,9 +849,14 @@ namespace AAXClasses AAX_Result GetParameterNumberofSteps (AAX_CParamID paramID, int32_t* result) const { if (isBypassParam (paramID)) + { *result = 2; + } else - *result = pluginInstance->getParameterNumSteps (getParamIndexFromID (paramID)); + { + if (auto* param = getParameterFromID (paramID)) + *result = param->getNumSteps(); + } return AAX_SUCCESS; } @@ -864,7 +866,11 @@ namespace AAXClasses if (isBypassParam (paramID)) return AAX_CEffectParameters::GetParameterNormalizedValue (paramID, result); - *result = pluginInstance->getParameter (getParamIndexFromID (paramID)); + if (auto* param = getParameterFromID (paramID)) + *result = (double) param->getValue(); + else + *result = 0.0; + return AAX_SUCCESS; } @@ -876,7 +882,7 @@ namespace AAXClasses if (auto* p = mParameterManager.GetParameterByID (paramID)) p->SetValueWithFloat ((float) newValue); - setAudioProcessorParameter (getParamIndexFromID (paramID), (float) newValue); + setAudioProcessorParameter (paramID, (float) newValue); return AAX_SUCCESS; } @@ -886,13 +892,15 @@ namespace AAXClasses if (isBypassParam (paramID)) return AAX_CEffectParameters::SetParameterNormalizedRelative (paramID, newDeltaValue); - auto paramIndex = getParamIndexFromID (paramID); - auto newValue = pluginInstance->getParameter (paramIndex) + (float) newDeltaValue; + if (auto* param = getParameterFromID (paramID)) + { + auto newValue = param->getValue() + (float) newDeltaValue; - setAudioProcessorParameter (paramIndex, jlimit (0.0f, 1.0f, newValue)); + setAudioProcessorParameter (paramID, jlimit (0.0f, 1.0f, newValue)); - if (auto* p = mParameterManager.GetParameterByID (paramID)) - p->SetValueWithFloat (newValue); + if (auto* p = mParameterManager.GetParameterByID (paramID)) + p->SetValueWithFloat (newValue); + } return AAX_SUCCESS; } @@ -900,11 +908,15 @@ namespace AAXClasses AAX_Result GetParameterNameOfLength (AAX_CParamID paramID, AAX_IString* result, int32_t maxLen) const override { if (isBypassParam (paramID)) + { result->Set (maxLen >= 13 ? "Master Bypass" : (maxLen >= 8 ? "Mast Byp" : (maxLen >= 6 ? "MstByp" : "MByp"))); - else - result->Set (pluginInstance->getParameterName (getParamIndexFromID (paramID), maxLen).toRawUTF8()); + } + else if (auto* param = getParameterFromID (paramID)) + { + result->Set (param->getName (maxLen).toRawUTF8()); + } return AAX_SUCCESS; } @@ -913,8 +925,8 @@ namespace AAXClasses { if (isBypassParam (paramID)) result->Set ("Master Bypass"); - else - result->Set (pluginInstance->getParameterName (getParamIndexFromID (paramID), 31).toRawUTF8()); + else if (auto* param = getParameterFromID (paramID)) + result->Set (param->getName (31).toRawUTF8()); return AAX_SUCCESS; } @@ -923,7 +935,10 @@ namespace AAXClasses { if (! isBypassParam (paramID)) { - *result = (double) pluginInstance->getParameterDefaultValue (getParamIndexFromID (paramID)); + if (auto* param = getParameterFromID (paramID)) + *result = (double) param->getDefaultValue(); + else + *result = 0.0; jassert (*result >= 0 && *result <= 1.0f); } @@ -1014,13 +1029,13 @@ namespace AAXClasses { ++mNumPlugInChanges; - auto numParameters = pluginInstance->getNumParameters(); + auto numParameters = juceParameters.getNumParameters(); for (int i = 0; i < numParameters; ++i) { if (auto* p = mParameterManager.GetParameterByID (getAAXParamIDFromJuceIndex (i))) { - auto newName = processor->getParameterName (i, 31); + auto newName = juceParameters.getParamForIndex (i)->getName (31); if (p->Name() != newName.toRawUTF8()) p->SetName (AAX_CString (newName.toRawUTF8())); @@ -1158,7 +1173,7 @@ namespace AAXClasses if (meterBuffers != nullptr) for (int i = 0; i < numMeters; ++i) - meterBuffers[i] = pluginInstance->getParameter (aaxMeters[i]); + meterBuffers[i] = aaxMeters[i]->getValue(); } } @@ -1422,53 +1437,56 @@ namespace AAXClasses void addAudioProcessorParameters() { auto& audioProcessor = getPluginInstance(); - auto numParameters = audioProcessor.getNumParameters(); #if JUCE_FORCE_USE_LEGACY_PARAM_IDS - const bool usingManagedParameters = false; + const bool forceLegacyParamIDs = true; #else - const bool usingManagedParameters = (audioProcessor.getParameters().size() == numParameters); + const bool forceLegacyParamIDs = false; #endif - for (int parameterIndex = 0; parameterIndex < numParameters; ++parameterIndex) + juceParameters.update (audioProcessor, forceLegacyParamIDs); + int parameterIndex = 0; + + for (auto* juceParam : juceParameters.params) { - auto category = audioProcessor.getParameterCategory (parameterIndex); + auto category = juceParam->getCategory(); + auto paramID = juceParameters.getParamID (audioProcessor, parameterIndex) + .toRawUTF8(); - aaxParamIDs.add (usingManagedParameters ? audioProcessor.getParameterID (parameterIndex) - : String (parameterIndex)); + aaxParamIDs.add (paramID); + auto aaxParamID = aaxParamIDs.getReference (parameterIndex++).getCharPointer(); - auto paramID = aaxParamIDs.getReference (parameterIndex).getCharPointer(); - paramMap.set (AAXClasses::getAAXParamHash (paramID), parameterIndex); + paramMap.set (AAXClasses::getAAXParamHash (aaxParamID), juceParam); // is this a meter? if (((category & 0xffff0000) >> 16) == 2) { - aaxMeters.add (parameterIndex); + aaxMeters.add (juceParam); continue; } - auto parameter = new AAX_CParameter (paramID, - AAX_CString (audioProcessor.getParameterName (parameterIndex, 31).toRawUTF8()), - audioProcessor.getParameterDefaultValue (parameterIndex), + auto parameter = new AAX_CParameter (aaxParamID, + AAX_CString (juceParam->getName (31).toRawUTF8()), + juceParam->getDefaultValue(), AAX_CLinearTaperDelegate(), AAX_CNumberDisplayDelegate(), - audioProcessor.isParameterAutomatable (parameterIndex)); + juceParam->isAutomatable()); - parameter->AddShortenedName (audioProcessor.getParameterName (parameterIndex, 4).toRawUTF8()); + parameter->AddShortenedName (juceParam->getName (4).toRawUTF8()); - auto parameterNumSteps = audioProcessor.getParameterNumSteps (parameterIndex); + auto parameterNumSteps = juceParam->getNumSteps(); parameter->SetNumberOfSteps ((uint32_t) parameterNumSteps); #if JUCE_FORCE_LEGACY_PARAMETER_AUTOMATION_TYPE parameter->SetType (parameterNumSteps > 1000 ? AAX_eParameterType_Continuous : AAX_eParameterType_Discrete); #else - parameter->SetType (audioProcessor.isParameterDiscrete (parameterIndex) ? AAX_eParameterType_Discrete - : AAX_eParameterType_Continuous); + parameter->SetType (juceParam->isDiscrete() ? AAX_eParameterType_Discrete + : AAX_eParameterType_Continuous); #endif - parameter->SetOrientation (audioProcessor.isParameterOrientationInverted (parameterIndex) + parameter->SetOrientation (juceParam->isOrientationInverted() ? (AAX_eParameterOrientation_RightMinLeftMax | AAX_eParameterOrientation_TopMinBottomMax | AAX_eParameterOrientation_RotarySingleDotMode | AAX_eParameterOrientation_RotaryRightMinLeftMax) : (AAX_eParameterOrientation_LeftMinRightMax | AAX_eParameterOrientation_BottomMinTopMax @@ -1696,7 +1714,10 @@ namespace AAXClasses //============================================================================== inline int getParamIndexFromID (AAX_CParamID paramID) const noexcept { - return paramMap [AAXClasses::getAAXParamHash (paramID)]; + if (auto* param = getParameterFromID (paramID)) + return LegacyAudioParameter::getParamIndex (getPluginInstance(), param); + + return -1; } inline AAX_CParamID getAAXParamIDFromJuceIndex (int index) const noexcept @@ -1707,6 +1728,11 @@ namespace AAXClasses return nullptr; } + AudioProcessorParameter* getParameterFromID (AAX_CParamID paramID) const noexcept + { + return paramMap [AAXClasses::getAAXParamHash (paramID)]; + } + //============================================================================== static AudioProcessor::BusesLayout getDefaultLayout (const AudioProcessor& p, bool enableAll) { @@ -1757,9 +1783,10 @@ namespace AAXClasses Array inputLayoutMap, outputLayoutMap; Array aaxParamIDs; - HashMap paramMap; + HashMap paramMap; + LegacyAudioParametersWrapper juceParameters; - Array aaxMeters; + Array aaxMeters; struct ChunkMemoryBlock { @@ -1835,12 +1862,21 @@ namespace AAXClasses //============================================================================== static int addAAXMeters (AudioProcessor& p, AAX_IEffectDescriptor& descriptor) { - const int n = p.getNumParameters(); + LegacyAudioParametersWrapper params; + + #if JUCE_FORCE_USE_LEGACY_PARAM_IDS + const bool forceLegacyParamIDs = true; + #else + const bool forceLegacyParamIDs = false; + #endif + + params.update (p, forceLegacyParamIDs); + int meterIdx = 0; - for (int i = 0; i < n; ++i) + for (auto* param : params.params) { - auto category = p.getParameterCategory (i); + auto category = param->getCategory(); // is this a meter? if (((category & 0xffff0000) >> 16) == 2) @@ -1851,7 +1887,7 @@ namespace AAXClasses meterProperties->AddProperty (AAX_eProperty_Meter_Orientation, AAX_eMeterOrientation_TopRight); descriptor.AddMeterDescription ('Metr' + static_cast (meterIdx++), - p.getParameterName (i).toRawUTF8(), meterProperties); + param->getName (1024).toRawUTF8(), meterProperties); } } } 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 417b1910b9..a2d77db24b 100644 --- a/modules/juce_audio_plugin_client/AU/juce_AU_Wrapper.mm +++ b/modules/juce_audio_plugin_client/AU/juce_AU_Wrapper.mm @@ -81,6 +81,7 @@ #include "../utility/juce_CarbonVisibility.h" #include "../../juce_audio_basics/native/juce_mac_CoreAudioLayouts.h" +#include "../../juce_audio_processors/format_types/juce_LegacyAudioParameter.cpp" #include "../../juce_audio_processors/format_types/juce_AU_Shared.h" //============================================================================== @@ -527,15 +528,18 @@ public: { if (juceFilter != nullptr) { - const int i = getJuceIndexForAUParameterID (pv->inParamID); - const String text (String::fromCFString (pv->inString)); + if (auto* param = getParameterForAUParameterID (pv->inParamID)) + { + const String text (String::fromCFString (pv->inString)); - if (AudioProcessorParameter* param = juceFilter->getParameters()[i]) - pv->outValue = param->getValueForText (text) * getMaximumParameterValue (i); - else - pv->outValue = text.getFloatValue(); + if (LegacyAudioParameter::isLegacy (param)) + pv->outValue = text.getFloatValue(); + else + pv->outValue = param->getValueForText (text) * getMaximumParameterValue (param); - return noErr; + + return noErr; + } } } } @@ -547,18 +551,20 @@ public: { if (juceFilter != nullptr) { - const int i = getJuceIndexForAUParameterID (pv->inParamID); - const float value = (float) *(pv->inValue); - String text; + if (auto* param = getParameterForAUParameterID (pv->inParamID)) + { + const float value = (float) *(pv->inValue); + String text; - if (AudioProcessorParameter* param = juceFilter->getParameters()[i]) - text = param->getText (value / getMaximumParameterValue (i), 0); - else - text = String (value); + if (LegacyAudioParameter::isLegacy (param)) + text = String (value); + else + text = param->getText (value / getMaximumParameterValue (param), 0); - pv->outString = text.toCFString(); + pv->outString = text.toCFString(); - return noErr; + return noErr; + } } } } @@ -793,7 +799,7 @@ public: if (inLayout == nullptr) return kAudioUnitErr_InvalidPropertyValue; - if (const AUIOElement* ioElement = GetIOElement (isInput ? kAudioUnitScope_Input : kAudioUnitScope_Output, element)) + if (const AUIOElement* ioElement = GetIOElement (isInput ? kAudioUnitScope_Input : kAudioUnitScope_Output, element)) { const AudioChannelSet newChannelSet = CoreAudioLayouts::fromCoreAudio (*inLayout); const int currentNumChannels = static_cast (ioElement->GetStreamFormat().NumberChannels()); @@ -825,82 +831,73 @@ public: //============================================================================== // When parameters are discrete we need to use integer values. - float getMaximumParameterValue (int parameterIndex) + float getMaximumParameterValue (AudioProcessorParameter* param) { - #if JUCE_FORCE_LEGACY_PARAMETER_AUTOMATION_TYPE - ignoreUnused (parameterIndex); - return 1.0f; - #else - return juceFilter->isParameterDiscrete (parameterIndex) ? (float) (juceFilter->getParameterNumSteps (parameterIndex) - 1) : 1.0f; - #endif + return param->isDiscrete() && (! forceUseLegacyParamIDs) ? (float) (param->getNumSteps() - 1) : 1.0f; } ComponentResult GetParameterInfo (AudioUnitScope inScope, AudioUnitParameterID inParameterID, AudioUnitParameterInfo& outParameterInfo) override { - const int index = getJuceIndexForAUParameterID (inParameterID); - - if (inScope == kAudioUnitScope_Global - && juceFilter != nullptr - && isPositiveAndBelow (index, juceFilter->getNumParameters())) + if (inScope == kAudioUnitScope_Global && juceFilter != nullptr) { - outParameterInfo.unit = kAudioUnitParameterUnit_Generic; - outParameterInfo.flags = (UInt32) (kAudioUnitParameterFlag_IsWritable - | kAudioUnitParameterFlag_IsReadable - | kAudioUnitParameterFlag_HasCFNameString - | kAudioUnitParameterFlag_ValuesHaveStrings); - - #if ! JUCE_FORCE_LEGACY_PARAMETER_AUTOMATION_TYPE - outParameterInfo.flags |= (UInt32) kAudioUnitParameterFlag_IsHighResolution; - #endif - - const String name (juceFilter->getParameterName (index)); - - // Set whether the param is automatable (unnamed parameters aren't allowed to be automated) - if (name.isEmpty() || ! juceFilter->isParameterAutomatable (index)) - outParameterInfo.flags |= kAudioUnitParameterFlag_NonRealTime; - - const bool isParameterDiscrete = juceFilter->isParameterDiscrete (index); - - if (! isParameterDiscrete) - outParameterInfo.flags |= kAudioUnitParameterFlag_CanRamp; - - if (juceFilter->isMetaParameter (index)) - outParameterInfo.flags |= kAudioUnitParameterFlag_IsGlobalMeta; - - // Is this a meter? - if (((juceFilter->getParameterCategory (index) & 0xffff0000) >> 16) == 2) - { - outParameterInfo.flags &= ~kAudioUnitParameterFlag_IsWritable; - outParameterInfo.flags |= kAudioUnitParameterFlag_MeterReadOnly | kAudioUnitParameterFlag_DisplayLogarithmic; - outParameterInfo.unit = kAudioUnitParameterUnit_LinearGain; - } - else + if (auto* param = getParameterForAUParameterID (inParameterID)) { + outParameterInfo.unit = kAudioUnitParameterUnit_Generic; + outParameterInfo.flags = (UInt32) (kAudioUnitParameterFlag_IsWritable + | kAudioUnitParameterFlag_IsReadable + | kAudioUnitParameterFlag_HasCFNameString + | kAudioUnitParameterFlag_ValuesHaveStrings); + #if ! JUCE_FORCE_LEGACY_PARAMETER_AUTOMATION_TYPE - if (isParameterDiscrete) - { - outParameterInfo.unit = kAudioUnitParameterUnit_Indexed; + outParameterInfo.flags |= (UInt32) kAudioUnitParameterFlag_IsHighResolution; + #endif - if (auto* param = juceFilter->getParameters()[index]) + const String name = param->getName (1024); + + // Set whether the param is automatable (unnamed parameters aren't allowed to be automated) + if (name.isEmpty() || ! param->isAutomatable()) + outParameterInfo.flags |= kAudioUnitParameterFlag_NonRealTime; + + const bool isParameterDiscrete = param->isDiscrete(); + + if (! isParameterDiscrete) + outParameterInfo.flags |= kAudioUnitParameterFlag_CanRamp; + + if (param->isMetaParameter()) + outParameterInfo.flags |= kAudioUnitParameterFlag_IsGlobalMeta; + + // Is this a meter? + if (((param->getCategory() & 0xffff0000) >> 16) == 2) + { + outParameterInfo.flags &= ~kAudioUnitParameterFlag_IsWritable; + outParameterInfo.flags |= kAudioUnitParameterFlag_MeterReadOnly | kAudioUnitParameterFlag_DisplayLogarithmic; + outParameterInfo.unit = kAudioUnitParameterUnit_LinearGain; + } + else + { + #if ! JUCE_FORCE_LEGACY_PARAMETER_AUTOMATION_TYPE + if (isParameterDiscrete) { + outParameterInfo.unit = kAudioUnitParameterUnit_Indexed; + if (param->isBoolean()) outParameterInfo.unit = kAudioUnitParameterUnit_Boolean; } + #endif } - #endif + + MusicDeviceBase::FillInParameterName (outParameterInfo, name.toCFString(), true); + + outParameterInfo.minValue = 0.0f; + outParameterInfo.maxValue = getMaximumParameterValue (param); + outParameterInfo.defaultValue = param->getDefaultValue() * getMaximumParameterValue (param); + jassert (outParameterInfo.defaultValue >= outParameterInfo.minValue + && outParameterInfo.defaultValue <= outParameterInfo.maxValue); + + return noErr; } - - MusicDeviceBase::FillInParameterName (outParameterInfo, name.toCFString(), true); - - outParameterInfo.minValue = 0.0f; - outParameterInfo.maxValue = getMaximumParameterValue (index); - outParameterInfo.defaultValue = juceFilter->getParameterDefaultValue (index) * getMaximumParameterValue (index); - jassert (outParameterInfo.defaultValue >= outParameterInfo.minValue - && outParameterInfo.defaultValue <= outParameterInfo.maxValue); - - return noErr; } return kAudioUnitErr_InvalidParameter; @@ -913,21 +910,24 @@ public: if (outStrings == nullptr) return noErr; - const int index = getJuceIndexForAUParameterID (inParameterID); - - if (inScope == kAudioUnitScope_Global - && juceFilter != nullptr - && isPositiveAndBelow (index, juceFilter->getNumParameters()) - && juceFilter->isParameterDiscrete (index)) + if (inScope == kAudioUnitScope_Global && juceFilter != nullptr) { - if (auto* valueStrings = parameterValueStringArrays[index]) + if (auto* param = getParameterForAUParameterID (inParameterID)) { - *outStrings = CFArrayCreate (NULL, - (const void **) valueStrings->getRawDataPointer(), - valueStrings->size(), - NULL); + if (param->isDiscrete()) + { + auto index = LegacyAudioParameter::getParamIndex (*juceFilter, param); - return noErr; + if (auto* valueStrings = parameterValueStringArrays[index]) + { + *outStrings = CFArrayCreate (NULL, + (const void **) valueStrings->getRawDataPointer(), + valueStrings->size(), + NULL); + + return noErr; + } + } } } @@ -941,11 +941,13 @@ public: { if (inScope == kAudioUnitScope_Global && juceFilter != nullptr) { - const auto index = getJuceIndexForAUParameterID (inID); - const auto normValue = juceFilter->getParameter (index); + if (auto* param = getParameterForAUParameterID (inID)) + { + const auto normValue = param->getValue(); - outValue = normValue * getMaximumParameterValue (index); - return noErr; + outValue = normValue * getMaximumParameterValue (param); + return noErr; + } } return MusicDeviceBase::GetParameter (inID, inScope, inElement, outValue); @@ -959,22 +961,17 @@ public: { if (inScope == kAudioUnitScope_Global && juceFilter != nullptr) { - auto index = getJuceIndexForAUParameterID (inID); - auto value = inValue / getMaximumParameterValue (index); - - if (auto* param = juceFilter->getParameters()[index]) + if (auto* param = getParameterForAUParameterID (inID)) { + auto value = inValue / getMaximumParameterValue (param); + param->setValue (value); inParameterChangedCallback = true; param->sendValueChangedMessageToListeners (value); - } - else - { - juceFilter->setParameter (index, value); - } - return noErr; + return noErr; + } } return MusicDeviceBase::SetParameter (inID, inScope, inElement, inValue, inBufferOffsetInFrames); @@ -1660,12 +1657,17 @@ private: bool prepared, isBypassed; //============================================================================== - #if ! JUCE_FORCE_USE_LEGACY_PARAM_IDS - bool usingManagedParameter; - Array auParamIDs; - HashMap paramMap; + #if JUCE_FORCE_USE_LEGACY_PARAM_IDS + static constexpr bool forceUseLegacyParamIDs = true; + #else + static constexpr bool forceUseLegacyParamIDs = false; #endif + //============================================================================== + LegacyAudioParametersWrapper juceParameters; + HashMap paramMap; + Array auParamIDs; + //============================================================================== AudioUnitEvent auEvent; mutable Array presetsArray; @@ -1814,109 +1816,95 @@ private: //============================================================================== void addParameters() { + juceParameters.update (*juceFilter, forceUseLegacyParamIDs); + // check if all parameters are managed? - const int numParams = juceFilter->getNumParameters(); + const int numParams = juceParameters.getNumParameters(); - #if ! JUCE_FORCE_USE_LEGACY_PARAM_IDS - usingManagedParameter = (juceFilter->getParameters().size() == numParams); - - if (usingManagedParameter) + for (auto* param : juceParameters.params) { - for (int i = 0; i < numParams; ++i) - { - const AudioUnitParameterID auParamID = generateAUParameterIDForIndex (i); + const AudioUnitParameterID auParamID = generateAUParameterID (param); - // Consider yourself very unlucky if you hit this assertion. The hash code of your - // parameter ids are not unique. - jassert (! paramMap.contains (static_cast (auParamID))); + // Consider yourself very unlucky if you hit this assertion. The hash code of your + // parameter ids are not unique. + jassert (! paramMap.contains (static_cast (auParamID))); - auParamIDs.add (auParamID); - paramMap.set (static_cast (auParamID), i); + auParamIDs.add (auParamID); + paramMap.set (static_cast (auParamID), param); - Globals()->SetParameter (auParamID, juceFilter->getParameter (i)); - } + if (juceParameters.isUsingManagedParameters()) + Globals()->SetParameter (auParamID, param->getValue()); } - else - #endif - { + + if (! juceParameters.isUsingManagedParameters()) Globals()->UseIndexedParameters (numParams); - } + #if JUCE_DEBUG // Some hosts can't handle the huge numbers of discrete parameter values created when // using the default number of steps. - for (auto* param : juceFilter->getParameters()) + for (auto* param : juceParameters.params) if (param->isDiscrete()) jassert (param->getNumSteps() != AudioProcessor::getDefaultNumParameterSteps()); #endif parameterValueStringArrays.ensureStorageAllocated (numParams); - for (int index = 0; index < numParams; ++index) + for (auto* param : juceParameters.params) { OwnedArray* stringValues = nullptr; - #if ! JUCE_FORCE_LEGACY_PARAMETER_AUTOMATION_TYPE - if (auto* param = juceFilter->getParameters()[index]) + auto initialValue = param->getValue(); + + if (param->isDiscrete() && (! forceUseLegacyParamIDs)) { - if (param->isDiscrete()) + const auto numSteps = param->getNumSteps(); + stringValues = new OwnedArray(); + stringValues->ensureStorageAllocated (numSteps); + + const auto maxValue = getMaximumParameterValue (param); + + for (int i = 0; i < numSteps; ++i) { - const auto numSteps = param->getNumSteps(); - stringValues = new OwnedArray(); - stringValues->ensureStorageAllocated (numSteps); + auto value = (float) i / maxValue; - const auto maxValue = getMaximumParameterValue (index); - - for (int i = 0; i < numSteps; ++i) - stringValues->add (CFStringCreateCopy (nullptr, (param->getText ((float) i / maxValue, 0)).toCFString())); + // Once legacy parameters are deprecated this can be replaced by getText + param->setValue (value); + stringValues->add (CFStringCreateCopy (nullptr, (param->getCurrentValueAsText().toCFString()))); } } - #endif + + param->setValue (initialValue); parameterValueStringArrays.add (stringValues); } } //============================================================================== - #if JUCE_FORCE_USE_LEGACY_PARAM_IDS - inline AudioUnitParameterID getAUParameterIDForIndex (int paramIndex) const noexcept { return static_cast (paramIndex); } - inline int getJuceIndexForAUParameterID (AudioUnitParameterID address) const noexcept { return static_cast (address); } - #else - AudioUnitParameterID generateAUParameterIDForIndex (int paramIndex) const + AudioUnitParameterID generateAUParameterID (AudioProcessorParameter* param) const { - const int n = juceFilter->getNumParameters(); + const String& juceParamID = LegacyAudioParameter::getParamID (param, forceUseLegacyParamIDs); + AudioUnitParameterID paramHash = static_cast (juceParamID.hashCode()); - if (isPositiveAndBelow (paramIndex, n)) - { - const String& juceParamID = juceFilter->getParameterID (paramIndex); + #if JUCE_USE_STUDIO_ONE_COMPATIBLE_PARAMETERS + // studio one doesn't like negative parameters + paramHash &= ~(1 << (sizeof (AudioUnitParameterID) * 8 - 1)); + #endif - AudioUnitParameterID paramHash = static_cast (juceParamID.hashCode()); - - #if JUCE_USE_STUDIO_ONE_COMPATIBLE_PARAMETERS - // studio one doesn't like negative parameters - paramHash &= ~(1 << (sizeof (AudioUnitParameterID) * 8 - 1)); - #endif - - return usingManagedParameter ? paramHash - : static_cast (juceParamID.getIntValue()); - } - - return static_cast (-1); + return juceParameters.isUsingManagedParameters() ? paramHash + : static_cast (juceParamID.getIntValue()); } inline AudioUnitParameterID getAUParameterIDForIndex (int paramIndex) const noexcept { - return usingManagedParameter ? auParamIDs.getReference (paramIndex) - : static_cast (paramIndex); + return juceParameters.isUsingManagedParameters() ? auParamIDs.getReference (paramIndex) + : static_cast (paramIndex); } - inline int getJuceIndexForAUParameterID (AudioUnitParameterID address) const noexcept + AudioProcessorParameter* getParameterForAUParameterID (AudioUnitParameterID address) const noexcept { - return usingManagedParameter ? paramMap[static_cast (address)] - : static_cast (address); + return paramMap[static_cast (address)]; } - #endif - //============================================================================== OSStatus syncAudioUnitWithProcessor() @@ -1934,7 +1922,7 @@ private: addSupportedLayoutTags(); for (int i = 0; i < enabledInputs; ++i) - if ((err = syncAudioUnitWithChannelSet (true, i, juceFilter->getChannelLayoutOfBus (true, i))) != noErr) return err; + if ((err = syncAudioUnitWithChannelSet (true, i, juceFilter->getChannelLayoutOfBus (true, i))) != noErr) return err; for (int i = 0; i < enabledOutputs; ++i) if ((err = syncAudioUnitWithChannelSet (false, i, juceFilter->getChannelLayoutOfBus (false, i))) != noErr) return err; 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 4f30bfa526..a11025afb4 100644 --- a/modules/juce_audio_plugin_client/AU/juce_AUv3_Wrapper.mm +++ b/modules/juce_audio_plugin_client/AU/juce_AUv3_Wrapper.mm @@ -64,6 +64,7 @@ #include "../../juce_graphics/native/juce_mac_CoreGraphicsHelpers.h" #include "../../juce_audio_basics/native/juce_mac_CoreAudioLayouts.h" +#include "../../juce_audio_processors/format_types/juce_LegacyAudioParameter.cpp" #include "../../juce_audio_processors/format_types/juce_AU_Shared.h" #define JUCE_VIEWCONTROLLER_OBJC_NAME(x) JUCE_JOIN_MACRO (x, FactoryAUv3) @@ -909,7 +910,7 @@ public: return; } - if (isPositiveAndBelow (idx, getAudioProcessor().getNumParameters())) + if (isPositiveAndBelow (idx, juceParameters.getNumParameters())) { if (AUParameter* param = [paramTree parameterWithAddress: getAUParameterAddressForIndex (idx)]) { @@ -1121,14 +1122,13 @@ private: } // When parameters are discrete we need to use integer values. - float getMaximumParameterValue (int parameterIndex) + float getMaximumParameterValue (AudioProcessorParameter* juceParam) { #if JUCE_FORCE_LEGACY_PARAMETER_AUTOMATION_TYPE - ignoreUnused (parameterIndex); + ignoreUnused (juceParam); return 1.0f; #else - auto& processor = getAudioProcessor(); - return processor.isParameterDiscrete (parameterIndex) ? (float) (processor.getParameterNumSteps (parameterIndex) - 1) : 1.0f; + return juceParam->isDiscrete() ? (float) (juceParam->getNumSteps() - 1) : 1.0f; #endif } @@ -1139,17 +1139,16 @@ private: overviewParams = [[NSMutableArray alloc] init]; auto& processor = getAudioProcessor(); - const int n = processor.getNumParameters(); + juceParameters.update (processor, forceLegacyParamIDs); - #if ! JUCE_FORCE_USE_LEGACY_PARAM_IDS - // check if all parameters are managed? - usingManagedParameter = (processor.getParameters().size() == processor.getNumParameters()); - #endif + const int n = juceParameters.getNumParameters(); for (int idx = 0; idx < n; ++idx) { + auto* juceParam = juceParameters.getParamForIndex (idx); + const String identifier (idx); - const String name = processor.getParameterName (idx); + const String name = juceParam->getName (512); AudioUnitParameterUnit unit = kAudioUnitParameterUnit_Generic; AudioUnitParameterOptions flags = (UInt32) (kAudioUnitParameterFlag_IsWritable @@ -1157,27 +1156,26 @@ private: | kAudioUnitParameterFlag_HasCFNameString | kAudioUnitParameterFlag_ValuesHaveStrings); - #if ! JUCE_FORCE_LEGACY_PARAMETER_AUTOMATION_TYPE - flags |= (UInt32) kAudioUnitParameterFlag_IsHighResolution; - #endif + if (! forceLegacyParamIDs) + flags |= (UInt32) kAudioUnitParameterFlag_IsHighResolution; // set whether the param is automatable (unnamed parameters aren't allowed to be automated) - if (name.isEmpty() || ! processor.isParameterAutomatable (idx)) + if (name.isEmpty() || ! juceParam->isAutomatable()) flags |= kAudioUnitParameterFlag_NonRealTime; - const bool isParameterDiscrete = processor.isParameterDiscrete (idx); + const bool isParameterDiscrete = juceParam->isDiscrete(); if (! isParameterDiscrete) flags |= kAudioUnitParameterFlag_CanRamp; - if (processor.isMetaParameter (idx)) + if (juceParam->isMetaParameter()) 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) + if (((juceParam->getCategory() & 0xffff0000) >> 16) == 2) { flags &= ~kAudioUnitParameterFlag_IsWritable; flags |= kAudioUnitParameterFlag_MeterReadOnly | kAudioUnitParameterFlag_DisplayLogarithmic; @@ -1185,14 +1183,13 @@ private: } else { - #if ! JUCE_FORCE_LEGACY_PARAMETER_AUTOMATION_TYPE - if (auto* param = processor.getParameters()[idx]) + if (! forceLegacyParamIDs) { - if (param->isDiscrete()) + if (juceParam->isDiscrete()) { - unit = param->isBoolean() ? kAudioUnitParameterUnit_Boolean : kAudioUnitParameterUnit_Indexed; - auto maxValue = getMaximumParameterValue (idx); - auto numSteps = param->getNumSteps(); + unit = juceParam->isBoolean() ? kAudioUnitParameterUnit_Boolean : kAudioUnitParameterUnit_Indexed; + auto maxValue = getMaximumParameterValue (juceParam); + auto numSteps = juceParam->getNumSteps(); // Some hosts can't handle the huge numbers of discrete parameter values created when // using the default number of steps. @@ -1201,24 +1198,22 @@ private: valueStrings.reset ([NSMutableArray new]); for (int i = 0; i < numSteps; ++i) - [valueStrings.get() addObject: juceStringToNS (param->getText ((float) i / maxValue, 0))]; + [valueStrings.get() addObject: juceStringToNS (juceParam->getText ((float) i / maxValue, 0))]; } } - #endif } - #if JUCE_FORCE_USE_LEGACY_PARAM_IDS - AUParameterAddress address = static_cast (idx); - #else - AUParameterAddress address = generateAUParameterAddressForIndex (idx); + AUParameterAddress address = forceLegacyParamIDs ? static_cast (idx) + : generateAUParameterAddress (juceParam); + #if ! JUCE_FORCE_LEGACY_PARAMETER_AUTOMATION_TYPE // 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))); paramAddresses.add (address); paramMap.set (static_cast (address), idx); - #endif + #endif // create methods in AUParameterTree return unretained objects (!) -> see Apple header AUAudioUnitImplementation.h @@ -1226,14 +1221,14 @@ private: name: juceStringToNS (name) address: address min: 0.0f - max: getMaximumParameterValue (idx) + max: getMaximumParameterValue (juceParam) unit: unit unitName: nullptr flags: flags valueStrings: valueStrings.get() dependentParameters: nullptr] retain]; - [param.get() setValue: processor.getParameterDefaultValue (idx)]; + [param.get() setValue: juceParam->getDefaultValue()]; [params addObject: param]; [overviewParams addObject: [NSNumber numberWithUnsignedLongLong:address]]; @@ -1259,21 +1254,14 @@ private: } } - void setAudioProcessorParameter (int index, float value) + void setAudioProcessorParameter (AudioProcessorParameter* juceParam, float value) { - if (auto* param = getAudioProcessor().getParameters()[index]) + if (value != juceParam->getValue()) { - if (value != param->getValue()) - { - param->setValue (value); + juceParam->setValue (value); - inParameterChangedCallback = true; - param->sendValueChangedMessageToListeners (value); - } - } - else if (isPositiveAndBelow (index, getAudioProcessor().getNumParameters())) - { - getAudioProcessor().setParameter (index, value); + inParameterChangedCallback = true; + juceParam->sendValueChangedMessageToListeners (value); } } @@ -1329,9 +1317,9 @@ private: case AURenderEventParameterRamp: { const AUParameterEvent& paramEvent = event->parameter; - const int idx = getJuceParameterIndexForAUAddress (paramEvent.parameterAddress); - setAudioProcessorParameter (idx, paramEvent.value); + if (auto* p = getJuceParameterForAUAddress (paramEvent.parameterAddress)) + setAudioProcessorParameter (p, paramEvent.value); } break; @@ -1349,7 +1337,7 @@ private: jassert (static_cast (frameCount) <= getAudioProcessor().getBlockSize()); // process params - const int numParams = processor.getNumParameters(); + const int numParams = juceParameters.getNumParameters(); processEvents (realtimeEventListHead, numParams, static_cast (timestamp->mSampleTime)); if (lastTimeStamp.mSampleTime != timestamp->mSampleTime) @@ -1477,10 +1465,11 @@ private: { if (param != nullptr) { - int idx = getJuceParameterIndexForAUAddress ([param address]); - auto normalisedValue = value / getMaximumParameterValue (idx); - - setAudioProcessorParameter (idx, normalisedValue); + if (auto* p = getJuceParameterForAUAddress ([param address])) + { + auto normalisedValue = value / getMaximumParameterValue (p); + setAudioProcessorParameter (p, normalisedValue); + } } } @@ -1488,11 +1477,8 @@ private: { if (param != nullptr) { - const int idx = getJuceParameterIndexForAUAddress ([param address]); - auto& processor = getAudioProcessor(); - - if (isPositiveAndBelow (idx, processor.getNumParameters())) - return processor.getParameter (idx) * getMaximumParameterValue (idx); + if (auto* p = getJuceParameterForAUAddress ([param address])) + return p->getValue() * getMaximumParameterValue (p); } return 0; @@ -1509,13 +1495,13 @@ private: 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); + if (auto* p = getJuceParameterForAUAddress ([param address])) + { + if (LegacyAudioParameter::isLegacy (p)) + text = String (*value); + else + text = p->getText (*value / getMaximumParameterValue (p), 0); + } } return juceStringToNS (text); @@ -1525,14 +1511,15 @@ private: { if (param != nullptr && str != nullptr) { - const int idx = getJuceParameterIndexForAUAddress ([param address]); - auto& processor = getAudioProcessor(); - const String text (nsStringToJuce (str)); + if (auto* p = getJuceParameterForAUAddress ([param address])) + { + const String text (nsStringToJuce (str)); - if (auto* p = processor.getParameters()[idx]) - return p->getValueForText (text) * getMaximumParameterValue (idx); - else - return text.getFloatValue(); + if (LegacyAudioParameter::isLegacy (p)) + return text.getFloatValue(); + else + return p->getValueForText (text) * getMaximumParameterValue (p); + } } return 0; @@ -1543,34 +1530,32 @@ private: inline AUParameterAddress getAUParameterAddressForIndex (int paramIndex) const noexcept { return static_cast (paramIndex); } inline int getJuceParameterIndexForAUAddress (AUParameterAddress address) const noexcept { return static_cast (address); } #else - AUParameterAddress generateAUParameterAddressForIndex (int paramIndex) const - { - auto& processor = getAudioProcessor(); - const int n = processor.getNumParameters(); - - if (isPositiveAndBelow (paramIndex, n)) - { - const String& juceParamID = processor.getParameterID (paramIndex); - return usingManagedParameter ? static_cast (juceParamID.hashCode64()) - : static_cast (juceParamID.getIntValue()); - } - - return static_cast (-1); - } - inline AUParameterAddress getAUParameterAddressForIndex (int paramIndex) const noexcept { - return usingManagedParameter ? paramAddresses.getReference (paramIndex) - : static_cast (paramIndex); + return juceParameters.isUsingManagedParameters() ? paramAddresses.getReference (paramIndex) + : static_cast (paramIndex); } inline int getJuceParameterIndexForAUAddress (AUParameterAddress address) const noexcept { - return usingManagedParameter ? paramMap[static_cast (address)] - : static_cast (address); + return juceParameters.isUsingManagedParameters() ? paramMap[static_cast (address)] + : static_cast (address); } #endif + AUParameterAddress generateAUParameterAddress (AudioProcessorParameter* param) const + { + const String& juceParamID = LegacyAudioParameter::getParamID (param, forceLegacyParamIDs); + + return juceParameters.isUsingManagedParameters() ? static_cast (juceParamID.hashCode64()) + : static_cast (juceParamID.getIntValue()); + } + + AudioProcessorParameter* getJuceParameterForAUAddress (AUParameterAddress address) const noexcept + { + return juceParameters.getParamForIndex (getJuceParameterIndexForAUAddress (address)); + } + //============================================================================== static const double kDefaultSampleRate; @@ -1587,10 +1572,10 @@ private: ObjCBlock valueFromStringProvider; #if ! JUCE_FORCE_USE_LEGACY_PARAM_IDS - bool usingManagedParameter; Array paramAddresses; HashMap paramMap; #endif + LegacyAudioParametersWrapper juceParameters; // to avoid recursion on parameter changes, we need to add an // editor observer to do the parameter changes @@ -1620,6 +1605,11 @@ private: String contextName; ThreadLocalValue inParameterChangedCallback; + #if JUCE_FORCE_USE_LEGACY_PARAM_IDS + static constexpr bool forceLegacyParamIDs = true; + #else + static constexpr bool forceLegacyParamIDs = false; + #endif }; const double JuceAudioUnitv3::kDefaultSampleRate = 44100.0; diff --git a/modules/juce_audio_plugin_client/VST/juce_VST_Wrapper.cpp b/modules/juce_audio_plugin_client/VST/juce_VST_Wrapper.cpp index df83c1e5d3..be79a94052 100644 --- a/modules/juce_audio_plugin_client/VST/juce_VST_Wrapper.cpp +++ b/modules/juce_audio_plugin_client/VST/juce_VST_Wrapper.cpp @@ -79,6 +79,7 @@ #include "../utility/juce_FakeMouseMoveGenerator.h" #include "../utility/juce_WindowsHooks.h" +#include "../../juce_audio_processors/format_types/juce_LegacyAudioParameter.cpp" #include "../../juce_audio_processors/format_types/juce_VSTCommon.h" #ifdef _MSC_VER @@ -281,6 +282,8 @@ public: processor->setPlayHead (this); processor->addListener (this); + juceParameters.update (*processor, false); + memset (&vstEffect, 0, sizeof (vstEffect)); vstEffect.interfaceIdentifier = juceVstInterfaceIdentifier; vstEffect.dispatchFunction = dispatcherCB; @@ -288,7 +291,7 @@ public: vstEffect.setParameterValueFunction = setParameterCB; vstEffect.getParameterValueFunction = getParameterCB; vstEffect.numPrograms = jmax (1, af->getNumPrograms()); - vstEffect.numParameters = af->getNumParameters(); + vstEffect.numParameters = juceParameters.getNumParameters(); vstEffect.numInputChannels = maxNumInChannels; vstEffect.numOutputChannels = maxNumOutChannels; vstEffect.latency = processor->getLatencySamples(); @@ -706,11 +709,10 @@ public: //============================================================================== float getParameter (int32 index) const { - if (processor == nullptr) - return 0.0f; + if (auto* param = juceParameters.getParamForIndex (index)) + return param->getValue(); - jassert (isPositiveAndBelow (index, processor->getNumParameters())); - return processor->getParameter (index); + return 0.0f; } static float getParameterCB (VstEffectInterface* vstInterface, int32 index) @@ -720,20 +722,12 @@ public: void setParameter (int32 index, float value) { - if (processor != nullptr) + if (auto* param = juceParameters.getParamForIndex (index)) { - if (auto* param = processor->getParameters()[index]) - { - param->setValue (value); + param->setValue (value); - inParameterChangedCallback = true; - param->sendValueChangedMessageToListeners (value); - } - else - { - jassert (isPositiveAndBelow (index, processor->getNumParameters())); - processor->setParameter (index, value); - } + inParameterChangedCallback = true; + param->sendValueChangedMessageToListeners (value); } } @@ -1472,6 +1466,8 @@ private: VSTMidiEventList outgoingEvents; float editorScaleFactor = 1.0f; + LegacyAudioParametersWrapper juceParameters; + bool isProcessing = false, isBypassed = false, hasShutdown = false; bool firstProcessCallback = true, shouldDeleteEditor = false; @@ -1664,11 +1660,10 @@ private: pointer_sized_int handleGetParameterLabel (VstOpCodeArguments args) { - if (processor != nullptr) + if (auto* param = juceParameters.getParamForIndex (args.index)) { - jassert (isPositiveAndBelow (args.index, processor->getNumParameters())); // length should technically be kVstMaxParamStrLen, which is 8, but hosts will normally allow a bit more. - processor->getParameterLabel (args.index).copyToUTF8 ((char*) args.ptr, 24 + 1); + param->getLabel().copyToUTF8 ((char*) args.ptr, 24 + 1); } return 0; @@ -1676,11 +1671,10 @@ private: pointer_sized_int handleGetParameterText (VstOpCodeArguments args) { - if (processor != nullptr) + if (auto* param = juceParameters.getParamForIndex (args.index)) { - jassert (isPositiveAndBelow (args.index, processor->getNumParameters())); // length should technically be kVstMaxParamStrLen, which is 8, but hosts will normally allow a bit more. - processor->getParameterText (args.index, 24).copyToUTF8 ((char*) args.ptr, 24 + 1); + param->getCurrentValueAsText().copyToUTF8 ((char*) args.ptr, 24 + 1); } return 0; @@ -1688,11 +1682,10 @@ private: pointer_sized_int handleGetParameterName (VstOpCodeArguments args) { - if (processor != nullptr) + if (auto* param = juceParameters.getParamForIndex (args.index)) { - jassert (isPositiveAndBelow (args.index, processor->getNumParameters())); // length should technically be kVstMaxParamStrLen, which is 8, but hosts will normally allow a bit more. - processor->getParameterName (args.index, 32).copyToUTF8 ((char*) args.ptr, 32 + 1); + param->getName (32).copyToUTF8 ((char*) args.ptr, 32 + 1); } return 0; @@ -1823,20 +1816,20 @@ private: pointer_sized_int handleIsParameterAutomatable (VstOpCodeArguments args) { - if (processor == nullptr) - return 0; + if (auto* param = juceParameters.getParamForIndex (args.index)) + { + const bool isMeter = (((param->getCategory() & 0xffff0000) >> 16) == 2); + return (param->isAutomatable() && (! isMeter) ? 1 : 0); + } - const bool isMeter = (((processor->getParameterCategory (args.index) & 0xffff0000) >> 16) == 2); - return (processor->isParameterAutomatable (args.index) && (! isMeter) ? 1 : 0); + return 0; } pointer_sized_int handleParameterValueForText (VstOpCodeArguments args) { - if (processor != nullptr) + if (auto* param = juceParameters.getParamForIndex (args.index)) { - jassert (isPositiveAndBelow (args.index, processor->getNumParameters())); - - if (auto* param = processor->getParameters()[args.index]) + if (! LegacyAudioParameter::isLegacy (param)) { auto value = param->getValueForText (String::fromUTF8 ((char*) args.ptr)); param->setValue (value); @@ -2120,11 +2113,14 @@ private: { if (processor != nullptr && dest != nullptr) { - if (auto* param = processor->getParameters()[(int) paramIndex]) + if (auto* param = juceParameters.getParamForIndex ((int) paramIndex)) { - String text (param->getText (value, 1024)); - memcpy (dest, text.toRawUTF8(), ((size_t) text.length()) + 1); - return 0xbeef; + if (! LegacyAudioParameter::isLegacy (param)) + { + String text (param->getText (value, 1024)); + memcpy (dest, text.toRawUTF8(), ((size_t) text.length()) + 1); + return 0xbeef; + } } } diff --git a/modules/juce_audio_plugin_client/VST3/juce_VST3_Wrapper.cpp b/modules/juce_audio_plugin_client/VST3/juce_VST3_Wrapper.cpp index dd438c410f..2b4186a260 100644 --- a/modules/juce_audio_plugin_client/VST3/juce_VST3_Wrapper.cpp +++ b/modules/juce_audio_plugin_client/VST3/juce_VST3_Wrapper.cpp @@ -42,6 +42,7 @@ #include "../utility/juce_IncludeModuleHeaders.h" #include "../utility/juce_WindowsHooks.h" #include "../utility/juce_FakeMouseMoveGenerator.h" +#include "../../juce_audio_processors/format_types/juce_LegacyAudioParameter.cpp" #include "../../juce_audio_processors/format_types/juce_VST3Common.h" #ifndef JUCE_VST3_CAN_REPLACE_VST2 @@ -89,7 +90,12 @@ using namespace Steinberg; class JuceAudioProcessor : public FUnknown { public: - JuceAudioProcessor (AudioProcessor* source) noexcept : audioProcessor (source) {} + JuceAudioProcessor (AudioProcessor* source) noexcept + : audioProcessor (source) + { + setupParameters(); + } + virtual ~JuceAudioProcessor() {} AudioProcessor* get() const noexcept { return audioProcessor; } @@ -97,15 +103,85 @@ public: JUCE_DECLARE_VST3_COM_QUERY_METHODS JUCE_DECLARE_VST3_COM_REF_METHODS - static const FUID iid; + //============================================================================== + #if JUCE_FORCE_USE_LEGACY_PARAM_IDS + inline Vst::ParamID getVSTParamIDForIndex (int paramIndex) const noexcept { return static_cast (paramIndex); } + #else + inline Vst::ParamID getVSTParamIDForIndex (int paramIndex) const noexcept + { + return isUsingManagedParameters() ? vstParamIDs.getReference (paramIndex) + : static_cast (paramIndex); + } + #endif + AudioProcessorParameter* getParamForVSTParamID (Vst::ParamID paramID) const noexcept + { + return paramMap[static_cast (paramID)]; + } + + int getNumParameters() const noexcept { return vstParamIDs.size(); } + bool isUsingManagedParameters() const noexcept { return juceParameters.isUsingManagedParameters(); } + + //============================================================================== + static const FUID iid; + Array vstParamIDs; + Vst::ParamID bypassParamID = 0; bool isBypassed = false; private: + enum InternalParameters + { + paramBypass = 0x62797073 // 'byps' + }; + + //============================================================================== + void setupParameters() + { + #if JUCE_FORCE_USE_LEGACY_PARAM_IDS + const bool forceLegacyParamIDs = true; + #else + const bool forceLegacyParamIDs = false; + #endif + + juceParameters.update (*audioProcessor, forceLegacyParamIDs); + auto numParameters = juceParameters.getNumParameters(); + + int i = 0; + for (auto* juceParam : juceParameters.params) + { + Vst::ParamID vstParamID = forceLegacyParamIDs ? static_cast (i++) + : generateVSTParamIDForParam (juceParam); + + vstParamIDs.add (vstParamID); + paramMap.set (static_cast (vstParamID), juceParam); + } + + bypassParamID = static_cast (isUsingManagedParameters() ? paramBypass : numParameters); + } + + Vst::ParamID generateVSTParamIDForParam (AudioProcessorParameter* param) + { + auto juceParamID = LegacyAudioParameter::getParamID (param, false); + auto paramHash = static_cast (juceParamID.hashCode()); + + #if JUCE_USE_STUDIO_ONE_COMPATIBLE_PARAMETERS + // studio one doesn't like negative parameters + paramHash &= ~(1 << (sizeof (Vst::ParamID) * 8 - 1)); + #endif + + return isUsingManagedParameters() ? paramHash + : static_cast (juceParamID.getIntValue()); + } + + //============================================================================== Atomic refCount; ScopedPointer audioProcessor; ScopedJuceInitialiser_GUI libraryInitialiser; + //============================================================================== + LegacyAudioParametersWrapper juceParameters; + HashMap paramMap; + JuceAudioProcessor() = delete; JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (JuceAudioProcessor) }; @@ -197,39 +273,38 @@ public: enum InternalParameters { paramPreset = 0x70727374, // 'prst' - paramBypass = 0x62797073, // 'byps' paramMidiControllerOffset = 0x6d636d00 // 'mdm*' }; struct Param : public Vst::Parameter { - Param (AudioProcessor& p, int index, Vst::ParamID paramID) : owner (p), paramIndex (index) + Param (JuceVST3EditController& editController, AudioProcessorParameter& p, + Vst::ParamID vstParamID, bool forceLegacyParamIDs) + : owner (editController), param (p) { - info.id = paramID; + info.id = vstParamID; - toString128 (info.title, p.getParameterName (index)); - toString128 (info.shortTitle, p.getParameterName (index, 8)); - toString128 (info.units, p.getParameterLabel (index)); + toString128 (info.title, param.getName (128)); + toString128 (info.shortTitle, param.getName (8)); + toString128 (info.units, param.getLabel()); info.stepCount = (Steinberg::int32) 0; - #if ! JUCE_FORCE_LEGACY_PARAMETER_AUTOMATION_TYPE - if (p.isParameterDiscrete (index)) - #endif + if (! forceLegacyParamIDs && param.isDiscrete()) { - const int numSteps = p.getParameterNumSteps (index); + const int numSteps = param.getNumSteps(); info.stepCount = (Steinberg::int32) (numSteps > 0 && numSteps < 0x7fffffff ? numSteps - 1 : 0); } - info.defaultNormalizedValue = p.getParameterDefaultValue (index); + info.defaultNormalizedValue = param.getDefaultValue(); jassert (info.defaultNormalizedValue >= 0 && info.defaultNormalizedValue <= 1.0f); info.unitId = Vst::kRootUnitId; // Is this a meter? - if (((p.getParameterCategory (index) & 0xffff0000) >> 16) == 2) + if (((param.getCategory() & 0xffff0000) >> 16) == 2) info.flags = Vst::ParameterInfo::kIsReadOnly; else - info.flags = p.isParameterAutomatable (index) ? Vst::ParameterInfo::kCanAutomate : 0; + info.flags = param.isAutomatable() ? Vst::ParameterInfo::kCanAutomate : 0; valueNormalized = info.defaultNormalizedValue; } @@ -251,17 +326,10 @@ public: { auto value = static_cast (v); - if (auto* param = owner.getParameters()[paramIndex]) - { - param->setValue (value); + param.setValue (value); - inParameterChangedCallback = true; - param->sendValueChangedMessageToListeners (value); - } - else - { - owner.setParameter (paramIndex, value); - } + inParameterChangedCallback = true; + param.sendValueChangedMessageToListeners (value); } changed(); @@ -273,18 +341,14 @@ public: void toString (Vst::ParamValue value, Vst::String128 result) const override { - if (auto* p = owner.getParameters()[paramIndex]) - toString128 (result, p->getText ((float) value, 128)); - else - // remain backward-compatible with old JUCE code - toString128 (result, owner.getParameterText (paramIndex, 128)); + toString128 (result, param.getText ((float) value, 128)); } bool fromString (const Vst::TChar* text, Vst::ParamValue& outValueNormalized) const override { - if (auto* p = owner.getParameters()[paramIndex]) + if (! LegacyAudioParameter::isLegacy (¶m)) { - outValueNormalized = p->getValueForText (getStringFromVstTChars (text)); + outValueNormalized = param.getValueForText (getStringFromVstTChars (text)); return true; } @@ -300,8 +364,8 @@ public: Vst::ParamValue toNormalized (Vst::ParamValue v) const override { return v; } private: - AudioProcessor& owner; - int paramIndex; + JuceVST3EditController& owner; + AudioProcessorParameter& param; JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Param) }; @@ -502,12 +566,8 @@ public: // Cubase and Nuendo need to inform the host of the current parameter values if (auto* pluginInstance = getPluginInstance()) { - auto numParameters = pluginInstance->getNumParameters(); - - for (int i = 0; i < numParameters; ++i) - setParamNormalized (getVSTParamIDForIndex (i), (double) pluginInstance->getParameter (i)); - - setParamNormalized (bypassParamID, audioProcessor->isBypassed ? 1.0f : 0.0f); + for (auto vstParamId : audioProcessor->vstParamIDs) + setParamNormalized (vstParamId, audioProcessor->getParamForVSTParamID (vstParamId)->getValue()); auto numPrograms = pluginInstance->getNumPrograms(); @@ -601,10 +661,7 @@ public: } //============================================================================== - void audioProcessorParameterChangeGestureBegin (AudioProcessor*, int index) override { beginEdit (getVSTParamIDForIndex (index)); } - void audioProcessorParameterChangeGestureEnd (AudioProcessor*, int index) override { endEdit (getVSTParamIDForIndex (index)); } - - void audioProcessorParameterChanged (AudioProcessor*, int index, float newValue) override + void paramChanged (Vst::ParamID vstParamId, float newValue) { if (inParameterChangedCallback.get()) { @@ -613,8 +670,17 @@ public: } // NB: Cubase has problems if performEdit is called without setParamNormalized - EditController::setParamNormalized (getVSTParamIDForIndex (index), (double) newValue); - performEdit (getVSTParamIDForIndex (index), (double) newValue); + EditController::setParamNormalized (vstParamId, (double) newValue); + performEdit (vstParamId, (double) newValue); + } + + //============================================================================== + void audioProcessorParameterChangeGestureBegin (AudioProcessor*, int index) override { beginEdit (audioProcessor->getVSTParamIDForIndex (index)); } + void audioProcessorParameterChangeGestureEnd (AudioProcessor*, int index) override { endEdit (audioProcessor->getVSTParamIDForIndex (index)); } + + void audioProcessorParameterChanged (AudioProcessor*, int index, float newValue) override + { + paramChanged (audioProcessor->getVSTParamIDForIndex (index), newValue); } void audioProcessorChanged (AudioProcessor*) override @@ -641,6 +707,7 @@ public: private: friend class JuceVST3Component; + friend struct Param; //============================================================================== ComSmartPtr audioProcessor; @@ -657,53 +724,41 @@ private: Vst::ParamID midiControllerToParameter[numMIDIChannels][Vst::kCountCtrlNumber]; //============================================================================== - #if ! JUCE_FORCE_USE_LEGACY_PARAM_IDS - bool usingManagedParameter = false; - Array vstParamIDs; - #endif - Vst::ParamID bypassParamID; + Atomic vst3IsPlaying { 0 }; - //============================================================================== void setupParameters() { if (auto* pluginInstance = getPluginInstance()) { pluginInstance->addListener (this); - #if JUCE_FORCE_USE_LEGACY_PARAM_IDS - const bool usingManagedParameter = false; - #endif - if (parameters.getParameterCount() <= 0) { - auto numParameters = pluginInstance->getNumParameters(); + #if JUCE_FORCE_USE_LEGACY_PARAM_IDS + const bool forceLegacyParamIDs = true; + #else + const bool forceLegacyParamIDs = false; + #endif - #if ! JUCE_FORCE_USE_LEGACY_PARAM_IDS - usingManagedParameter = (pluginInstance->getParameters().size() == numParameters); - #endif + auto n = audioProcessor->getNumParameters(); - for (int i = 0; i < numParameters; ++i) + for (int i = 0; i < n; ++i) { - #if JUCE_FORCE_USE_LEGACY_PARAM_IDS - const Vst::ParamID vstParamID = static_cast (i); - #else - const Vst::ParamID vstParamID = generateVSTParamIDForIndex (pluginInstance, i); - vstParamIDs.add (vstParamID); - #endif + auto vstParamID = audioProcessor->getVSTParamIDForIndex (i); + auto* juceParam = audioProcessor->getParamForVSTParamID (vstParamID); - parameters.addParameter (new Param (*pluginInstance, i, vstParamID)); + parameters.addParameter (new Param (*this, *juceParam, vstParamID, forceLegacyParamIDs)); } - bypassParamID = static_cast (usingManagedParameter ? paramBypass : numParameters); - parameters.addParameter (new BypassParam (bypassParamID)); + parameters.addParameter (new BypassParam (audioProcessor->bypassParamID)); if (pluginInstance->getNumPrograms() > 1) parameters.addParameter (new ProgramChangeParameter (*pluginInstance)); } #if JUCE_VST3_EMULATE_MIDI_CC_WITH_PARAMETERS - parameterToMidiControllerOffset = static_cast (usingManagedParameter ? paramMidiControllerOffset - : parameters.getParameterCount()); + parameterToMidiControllerOffset = static_cast (audioProcessor->isUsingManagedParameters() ? paramMidiControllerOffset + : parameters.getParameterCount()); initialiseMidiControllerMappings(); #endif @@ -742,41 +797,6 @@ private: } } - //============================================================================== - #if JUCE_FORCE_USE_LEGACY_PARAM_IDS - inline Vst::ParamID getVSTParamIDForIndex (int paramIndex) const noexcept { return static_cast (paramIndex); } - #else - static Vst::ParamID generateVSTParamIDForIndex (AudioProcessor* const pluginFilter, int paramIndex) - { - jassert (pluginFilter != nullptr); - - const int n = pluginFilter->getNumParameters(); - const bool managedParameter = (pluginFilter->getParameters().size() == n); - - if (isPositiveAndBelow (paramIndex, n)) - { - auto juceParamID = pluginFilter->getParameterID (paramIndex); - auto paramHash = static_cast (juceParamID.hashCode()); - - #if JUCE_USE_STUDIO_ONE_COMPATIBLE_PARAMETERS - // studio one doesn't like negative parameters - paramHash &= ~(1 << (sizeof (Vst::ParamID) * 8 - 1)); - #endif - - return managedParameter ? paramHash - : static_cast (juceParamID.getIntValue()); - } - - return static_cast (-1); - } - - inline Vst::ParamID getVSTParamIDForIndex (int paramIndex) const noexcept - { - return usingManagedParameter ? vstParamIDs.getReference (paramIndex) - : static_cast (paramIndex); - } - #endif - //============================================================================== class JuceVST3Editor : public Vst::EditorView, public Steinberg::IPlugViewContentScaleSupport, @@ -1172,17 +1192,14 @@ public: processSetup.sampleRate = 44100.0; processSetup.symbolicSampleSize = Vst::kSample32; - #if JUCE_FORCE_USE_LEGACY_PARAM_IDS - vstBypassParameterId = static_cast (pluginInstance->getNumParameters()); - #else - cacheParameterIDs(); - #endif - pluginInstance->setPlayHead (this); } ~JuceVST3Component() { + if (juceVST3EditController != nullptr) + juceVST3EditController->vst3IsPlaying = 0; + if (pluginInstance != nullptr) if (pluginInstance->getPlayHead() == this) pluginInstance->setPlayHead (nullptr); @@ -1246,6 +1263,9 @@ public: tresult PLUGIN_API disconnect (IConnectionPoint*) override { + if (juceVST3EditController != nullptr) + juceVST3EditController->vst3IsPlaying = 0; + juceVST3EditController = nullptr; return kResultTrue; } @@ -2020,7 +2040,7 @@ public: { auto vstParamID = paramQueue->getParameterId(); - if (vstParamID == vstBypassParameterId) + if (vstParamID == comPluginInstance->bypassParamID) setBypassed (static_cast (value) != 0.0f); #if JUCE_VST3_EMULATE_MIDI_CC_WITH_PARAMETERS else if (juceVST3EditController->isMidiControllerParamID (vstParamID)) @@ -2037,20 +2057,15 @@ public: } else { - auto index = getJuceIndexForVSTParamID (vstParamID); auto floatValue = static_cast (value); - if (auto* param = pluginInstance->getParameters()[index]) + if (auto* param = comPluginInstance->getParamForVSTParamID (vstParamID)) { param->setValue (floatValue); inParameterChangedCallback = true; param->sendValueChangedMessageToListeners (floatValue); } - else if (isPositiveAndBelow (index, pluginInstance->getNumParameters())) - { - pluginInstance->setParameter (index, floatValue); - } } } } @@ -2088,12 +2103,16 @@ public: if (data.processContext != nullptr) { processContext = *data.processContext; - pluginInstance->vst3IsPlaying = processContext.state & Vst::ProcessContext::kPlaying; + + if (juceVST3EditController != nullptr) + juceVST3EditController->vst3IsPlaying = processContext.state & Vst::ProcessContext::kPlaying; } else { zerostruct (processContext); - pluginInstance->vst3IsPlaying = 0; + + if (juceVST3EditController != nullptr) + juceVST3EditController->vst3IsPlaying = 0; } midiBuffer.clear(); @@ -2164,14 +2183,6 @@ private: #endif ScopedJuceInitialiser_GUI libraryInitialiser; - - #if ! JUCE_FORCE_USE_LEGACY_PARAM_IDS - bool usingManagedParameter; - Array vstParamIDs; - HashMap paramMap; - #endif - Vst::ParamID vstBypassParameterId; - static const char* kJucePrivateDataIdentifier; //============================================================================== @@ -2387,44 +2398,6 @@ private: p.prepareToPlay (sampleRate, bufferSize); } - //============================================================================== - #if JUCE_FORCE_USE_LEGACY_PARAM_IDS - inline Vst::ParamID getVSTParamIDForIndex (int paramIndex) const noexcept { return static_cast (paramIndex); } - inline int getJuceIndexForVSTParamID (Vst::ParamID paramID) const noexcept { return static_cast (paramID); } - #else - void cacheParameterIDs() - { - const int numParameters = pluginInstance->getNumParameters(); - usingManagedParameter = (pluginInstance->getParameters().size() == numParameters); - - vstBypassParameterId = static_cast (usingManagedParameter ? JuceVST3EditController::paramBypass : numParameters); - - for (int i = 0; i < numParameters; ++i) - { - auto paramID = JuceVST3EditController::generateVSTParamIDForIndex (pluginInstance, i); - - // Consider yourself very unlucky if you hit this assertion. The hash code of your - // parameter ids are not unique. - jassert (! vstParamIDs.contains (paramID)); - - vstParamIDs.add (paramID); - paramMap.set (static_cast (paramID), i); - } - } - - inline Vst::ParamID getVSTParamIDForIndex (int paramIndex) const noexcept - { - return usingManagedParameter ? vstParamIDs.getReference (paramIndex) - : static_cast (paramIndex); - } - - inline int getJuceIndexForVSTParamID (Vst::ParamID paramID) const noexcept - { - return usingManagedParameter ? paramMap[static_cast (paramID)] - : static_cast (paramID); - } - #endif - //============================================================================== JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (JuceVST3Component) }; diff --git a/modules/juce_audio_processors/format_types/juce_LegacyAudioParameter.cpp b/modules/juce_audio_processors/format_types/juce_LegacyAudioParameter.cpp new file mode 100644 index 0000000000..aa0c5d6c2a --- /dev/null +++ b/modules/juce_audio_processors/format_types/juce_LegacyAudioParameter.cpp @@ -0,0 +1,185 @@ +/* + ============================================================================== + + This file is part of the JUCE library. + Copyright (c) 2017 - ROLI Ltd. + + JUCE is an open source library subject to commercial or open-source + licensing. + + By using JUCE, you agree to the terms of both the JUCE 5 End-User License + Agreement and JUCE 5 Privacy Policy (both updated and effective as of the + 27th April 2017). + + End User License Agreement: www.juce.com/juce-5-licence + Privacy Policy: www.juce.com/juce-5-privacy-policy + + Or: You may also use this code under the terms of the GPL v3 (see + www.gnu.org/licenses). + + JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER + EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE + DISCLAIMED. + + ============================================================================== +*/ + +namespace juce +{ + +#if JUCE_GCC + #pragma GCC diagnostic push + #pragma GCC diagnostic ignored "-Wdeprecated-declarations" +#elif JUCE_CLANG + #pragma clang diagnostic push + #pragma clang diagnostic ignored "-Wdeprecated-declarations" +#elif JUCE_MSVC + #pragma warning (push, 0) + #pragma warning (disable: 4996) +#endif + +class LegacyAudioParameter : public AudioProcessorParameter +{ +public: + LegacyAudioParameter (AudioProcessor& audioProcessorToUse, int audioParameterIndex) + : audioProcessor (audioProcessorToUse), idx (audioParameterIndex) + { + jassert (idx < audioProcessor.getNumParameters()); + } + + //============================================================================== + float getValue() const override { return audioProcessor.getParameter (idx); } + void setValue (float newValue) override { audioProcessor.setParameter (idx, newValue); } + float getDefaultValue() const override { return audioProcessor.getParameterDefaultValue (idx); } + String getName (int maxLen) const override { return audioProcessor.getParameterName (idx, maxLen); } + String getLabel() const override { return audioProcessor.getParameterLabel (idx); } + int getNumSteps() const override { return audioProcessor.getParameterNumSteps (idx); } + bool isDiscrete() const override { return audioProcessor.isParameterDiscrete (idx); } + bool isBoolean() const override { return false; } + bool isOrientationInverted() const override { return audioProcessor.isParameterOrientationInverted (idx); } + bool isAutomatable() const override { return audioProcessor.isParameterAutomatable (idx); } + bool isMetaParameter() const override { return audioProcessor.isMetaParameter (idx); } + Category getCategory() const override { return audioProcessor.getParameterCategory (idx); } + String getCurrentValueAsText() const override { return audioProcessor.getParameterText (idx); } + String getParamID() const { return audioProcessor.getParameterID (idx); } + + //============================================================================== + float getValueForText (const String&) const override + { + // legacy parameters do not support this method + jassertfalse; + return 0.0f; + } + + String getText (float, int) const override + { + // legacy parameters do not support this method + jassertfalse; + return {}; + } + + //============================================================================== + static bool isLegacy (AudioProcessorParameter* param) noexcept + { + return (dynamic_cast (param) != nullptr); + } + + static int getParamIndex (AudioProcessor& processor, AudioProcessorParameter* param) noexcept + { + if (auto* legacy = dynamic_cast (param)) + { + return legacy->idx; + } + else + { + auto n = processor.getNumParameters(); + jassert (n == processor.getParameters().size()); + + for (int i = 0; i < n; ++i) + { + if (processor.getParameters()[i] == param) + return i; + } + } + + return -1; + } + + static String getParamID (AudioProcessorParameter* param, bool forceLegacyParamIDs) noexcept + { + if (auto* legacy = dynamic_cast (param)) + { + return legacy->getParamID(); + } + else if (auto* paramWithID = dynamic_cast (param)) + { + if (! forceLegacyParamIDs) + return paramWithID->paramID; + } + + return String (param->getParameterIndex()); + } +private: + AudioProcessor& audioProcessor; + int idx; +}; + +//============================================================================== +class LegacyAudioParametersWrapper +{ +public: + void update (AudioProcessor& audioProcessor, bool forceLegacyParamIDs) + { + clear(); + + legacyParamIDs = forceLegacyParamIDs; + + auto numParameters = audioProcessor.getNumParameters(); + usingManagedParameters = (audioProcessor.getParameters().size() == numParameters) && (! legacyParamIDs); + + for (int i = 0; i < numParameters; ++i) + { + AudioProcessorParameter* param = usingManagedParameters ? audioProcessor.getParameters()[i] + : (legacy.add (new LegacyAudioParameter (audioProcessor, i))); + params.add (param); + } + } + + void clear() + { + legacy.clear(); + params.clear(); + } + + AudioProcessorParameter* getParamForIndex (int index) const + { + if (isPositiveAndBelow (index, params.size())) + return params[index]; + + return nullptr; + } + + String getParamID (AudioProcessor& processor, int idx) const noexcept + { + return usingManagedParameters ? processor.getParameterID (idx) : String (idx); + } + + bool isUsingManagedParameters() const noexcept { return usingManagedParameters; } + int getNumParameters() const noexcept { return params.size(); } + + Array params; + +private: + OwnedArray legacy; + bool legacyParamIDs = false, usingManagedParameters = false; +}; + +#if JUCE_GCC + #pragma GCC diagnostic pop +#elif JUCE_CLANG + #pragma clang diagnostic pop +#elif JUCE_MSVC + #pragma warning (pop) +#endif + +} // namespace juce diff --git a/modules/juce_audio_processors/juce_audio_processors.cpp b/modules/juce_audio_processors/juce_audio_processors.cpp index c5ef4a0b58..ed56fdb87d 100644 --- a/modules/juce_audio_processors/juce_audio_processors.cpp +++ b/modules/juce_audio_processors/juce_audio_processors.cpp @@ -154,6 +154,7 @@ struct AutoResizingNSViewComponentWithParent : public AutoResizingNSViewCompone #include "format/juce_AudioPluginFormat.cpp" #include "format/juce_AudioPluginFormatManager.cpp" +#include "format_types/juce_LegacyAudioParameter.cpp" #include "processors/juce_AudioProcessor.cpp" #include "processors/juce_AudioPluginInstance.cpp" #include "processors/juce_AudioProcessorEditor.cpp" diff --git a/modules/juce_audio_processors/processors/juce_AudioPluginInstance.h b/modules/juce_audio_processors/processors/juce_AudioPluginInstance.h index 5888a6102d..6190aa2557 100644 --- a/modules/juce_audio_processors/processors/juce_AudioPluginInstance.h +++ b/modules/juce_audio_processors/processors/juce_AudioPluginInstance.h @@ -27,6 +27,14 @@ namespace juce { +#if JUCE_MSVC + #pragma warning (push, 0) + + // MSVC does not like it if you override a deprecated method even if you + // keep the deprecation attribute. Other compilers are more forgiving. + #pragma warning (disable: 4996) +#endif + //============================================================================== /** Base class for an active instance of a plugin. @@ -123,4 +131,8 @@ private: JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AudioPluginInstance) }; +#if JUCE_MSVC + #pragma warning (pop) +#endif + } // namespace juce diff --git a/modules/juce_audio_processors/processors/juce_AudioProcessor.cpp b/modules/juce_audio_processors/processors/juce_AudioProcessor.cpp index 5e389ee10b..43658a5772 100644 --- a/modules/juce_audio_processors/processors/juce_AudioProcessor.cpp +++ b/modules/juce_audio_processors/processors/juce_AudioProcessor.cpp @@ -414,6 +414,18 @@ void AudioProcessor::setLatencySamples (const int newLatency) } } +//============================================================================== +#if JUCE_GCC + #pragma GCC diagnostic push + #pragma GCC diagnostic ignored "-Wdeprecated-declarations" +#elif JUCE_CLANG + #pragma clang diagnostic push + #pragma clang diagnostic ignored "-Wdeprecated-declarations" +#elif JUCE_MSVC + #pragma warning (push, 0) + #pragma warning (disable: 4996) +#endif + void AudioProcessor::setParameterNotifyingHost (int parameterIndex, float newValue) { if (auto* param = getParameters()[parameterIndex]) @@ -427,12 +439,6 @@ void AudioProcessor::setParameterNotifyingHost (int parameterIndex, float newVal } } -AudioProcessorListener* AudioProcessor::getListenerLocked (int index) const noexcept -{ - const ScopedLock sl (listenerLock); - return listeners[index]; -} - void AudioProcessor::sendParamChangeMessageToListeners (int parameterIndex, float newValue) { if (auto* param = getParameters()[parameterIndex]) @@ -511,6 +517,49 @@ void AudioProcessor::endParameterChangeGesture (int parameterIndex) } } +String AudioProcessor::getParameterName (int index, int maximumStringLength) +{ + if (auto* p = managedParameters[index]) + return p->getName (maximumStringLength); + + return getParameterName (index).substring (0, maximumStringLength); +} + +const String AudioProcessor::getParameterText (int index) +{ + #if JUCE_DEBUG + // if you hit this, then you're probably using the old parameter control methods, + // but have forgotten to implement either of the getParameterText() methods. + jassert (! textRecursionCheck); + ScopedValueSetter sv (textRecursionCheck, true, false); + #endif + + return getParameterText (index, 1024); +} + +String AudioProcessor::getParameterText (int index, int maximumStringLength) +{ + if (auto* p = managedParameters[index]) + return p->getText (p->getValue(), maximumStringLength); + + return getParameterText (index).substring (0, maximumStringLength); +} + +#if JUCE_GCC + #pragma GCC diagnostic pop +#elif JUCE_CLANG + #pragma clang diagnostic pop +#elif JUCE_MSVC + #pragma warning (pop) +#endif + +//============================================================================== +AudioProcessorListener* AudioProcessor::getListenerLocked (int index) const noexcept +{ + const ScopedLock sl (listenerLock); + return listeners[index]; +} + void AudioProcessor::updateHostDisplay() { for (int i = listeners.size(); --i >= 0;) @@ -567,34 +616,6 @@ String AudioProcessor::getParameterID (int index) return String (index); } -String AudioProcessor::getParameterName (int index, int maximumStringLength) -{ - if (auto* p = managedParameters[index]) - return p->getName (maximumStringLength); - - return getParameterName (index).substring (0, maximumStringLength); -} - -const String AudioProcessor::getParameterText (int index) -{ - #if JUCE_DEBUG - // if you hit this, then you're probably using the old parameter control methods, - // but have forgotten to implement either of the getParameterText() methods. - jassert (! textRecursionCheck); - ScopedValueSetter sv (textRecursionCheck, true, false); - #endif - - return getParameterText (index, 1024); -} - -String AudioProcessor::getParameterText (int index, int maximumStringLength) -{ - if (auto* p = managedParameters[index]) - return p->getText (p->getValue(), maximumStringLength); - - return getParameterText (index).substring (0, maximumStringLength); -} - int AudioProcessor::getParameterNumSteps (int index) { if (auto* p = managedParameters[index]) @@ -1368,9 +1389,6 @@ AudioProcessorParameter::~AudioProcessorParameter() void AudioProcessorParameter::setValueNotifyingHost (float newValue) { - // This method can't be used until the parameter has been attached to a processor! - jassert (processor != nullptr && parameterIndex >= 0); - setValue (newValue); sendValueChangedMessageToListeners (newValue); } @@ -1394,11 +1412,14 @@ void AudioProcessorParameter::beginChangeGesture() if (auto* l = listeners[i]) l->parameterGestureChanged (getParameterIndex(), true); - // audioProcessorParameterChangeGestureBegin callbacks will shortly be deprecated and - // this code will be removed. - for (int i = processor->listeners.size(); --i >= 0;) - if (auto* l = processor->listeners[i]) - l->audioProcessorParameterChangeGestureBegin (processor, getParameterIndex()); + if (processor != nullptr && parameterIndex >= 0) + { + // audioProcessorParameterChangeGestureBegin callbacks will shortly be deprecated and + // this code will be removed. + for (int i = processor->listeners.size(); --i >= 0;) + if (auto* l = processor->listeners[i]) + l->audioProcessorParameterChangeGestureBegin (processor, getParameterIndex()); + } } void AudioProcessorParameter::endChangeGesture() @@ -1420,11 +1441,14 @@ void AudioProcessorParameter::endChangeGesture() if (auto* l = listeners[i]) l->parameterGestureChanged (getParameterIndex(), false); - // audioProcessorParameterChangeGestureEnd callbacks will shortly be deprecated and - // this code will be removed. - for (int i = processor->listeners.size(); --i >= 0;) - if (auto* l = processor->listeners[i]) - l->audioProcessorParameterChangeGestureEnd (processor, getParameterIndex()); + if (processor != nullptr && parameterIndex >= 0) + { + // audioProcessorParameterChangeGestureEnd callbacks will shortly be deprecated and + // this code will be removed. + for (int i = processor->listeners.size(); --i >= 0;) + if (auto* l = processor->listeners[i]) + l->audioProcessorParameterChangeGestureEnd (processor, getParameterIndex()); + } } void AudioProcessorParameter::sendValueChangedMessageToListeners (float newValue) @@ -1435,11 +1459,14 @@ void AudioProcessorParameter::sendValueChangedMessageToListeners (float newValue if (auto* l = listeners [i]) l->parameterValueChanged (getParameterIndex(), newValue); - // audioProcessorParameterChanged callbacks will shortly be deprecated and - // this code will be removed. - for (int i = processor->listeners.size(); --i >= 0;) - if (auto* l = processor->listeners[i]) - l->audioProcessorParameterChanged (processor, getParameterIndex(), newValue); + if (processor != nullptr && parameterIndex >= 0) + { + // audioProcessorParameterChanged callbacks will shortly be deprecated and + // this code will be removed. + for (int i = processor->listeners.size(); --i >= 0;) + if (auto* l = processor->listeners[i]) + l->audioProcessorParameterChanged (processor, getParameterIndex(), newValue); + } } bool AudioProcessorParameter::isOrientationInverted() const { return false; } diff --git a/modules/juce_audio_processors/processors/juce_AudioProcessor.h b/modules/juce_audio_processors/processors/juce_AudioProcessor.h index c2c2cb633f..d44cd86b83 100644 --- a/modules/juce_audio_processors/processors/juce_AudioProcessor.h +++ b/modules/juce_audio_processors/processors/juce_AudioProcessor.h @@ -962,14 +962,14 @@ public: NOTE! This method will eventually be deprecated! It's recommended that you use the AudioProcessorParameter class instead to manage your parameters. */ - virtual int getNumParameters(); + JUCE_DEPRECATED (virtual int getNumParameters()); /** Returns the name of a particular parameter. NOTE! This method will eventually be deprecated! It's recommended that you use the AudioProcessorParameter class instead to manage your parameters. */ - virtual const String getParameterName (int parameterIndex); + JUCE_DEPRECATED (virtual const String getParameterName (int parameterIndex)); /** Returns the ID of a particular parameter. @@ -980,7 +980,7 @@ public: NOTE! This method will eventually be deprecated! It's recommended that you use the AudioProcessorParameterWithID class instead to manage your parameters. */ - virtual String getParameterID (int index); + JUCE_DEPRECATED (virtual String getParameterID (int index)); /** Called by the host to find out the value of one of the processor's parameters. @@ -993,7 +993,7 @@ public: NOTE! This method will eventually be deprecated! It's recommended that you use the AudioProcessorParameter class instead to manage your parameters. */ - virtual float getParameter (int parameterIndex); + JUCE_DEPRECATED (virtual float getParameter (int parameterIndex)); /** Returns the name of a parameter as a text string with a preferred maximum length. If you want to provide customised short versions of your parameter names that @@ -1005,13 +1005,13 @@ public: NOTE! This method will eventually be deprecated! It's recommended that you use AudioProcessorParameter::getName() instead. */ - virtual String getParameterName (int parameterIndex, int maximumStringLength); + JUCE_DEPRECATED (virtual String getParameterName (int parameterIndex, int maximumStringLength)); /** Returns the value of a parameter as a text string. NOTE! This method will eventually be deprecated! It's recommended that you use AudioProcessorParameter::getText() instead. */ - virtual const String getParameterText (int parameterIndex); + JUCE_DEPRECATED (virtual const String getParameterText (int parameterIndex)); /** Returns the value of a parameter as a text string with a preferred maximum length. If you want to provide customised short versions of your parameter values that @@ -1023,7 +1023,7 @@ public: NOTE! This method will eventually be deprecated! It's recommended that you use AudioProcessorParameter::getText() instead. */ - virtual String getParameterText (int parameterIndex, int maximumStringLength); + JUCE_DEPRECATED (virtual String getParameterText (int parameterIndex, int maximumStringLength)); /** Returns the number of discrete steps that this parameter can represent. @@ -1043,7 +1043,7 @@ public: @see isParameterDiscrete */ - virtual int getParameterNumSteps (int parameterIndex); + JUCE_DEPRECATED (virtual int getParameterNumSteps (int parameterIndex)); /** Returns the default number of steps for a parameter. @@ -1067,7 +1067,7 @@ public: @see getParameterNumSteps */ - virtual bool isParameterDiscrete (int parameterIndex) const; + JUCE_DEPRECATED (virtual bool isParameterDiscrete (int parameterIndex) const); /** Returns the default value for the parameter. By default, this just returns 0. @@ -1076,7 +1076,7 @@ public: NOTE! This method will eventually be deprecated! It's recommended that you use AudioProcessorParameter::getDefaultValue() instead. */ - virtual float getParameterDefaultValue (int parameterIndex); + JUCE_DEPRECATED (virtual float getParameterDefaultValue (int parameterIndex)); /** Some plugin types may be able to return a label string for a parameter's units. @@ -1084,7 +1084,7 @@ public: NOTE! This method will eventually be deprecated! It's recommended that you use AudioProcessorParameter::getLabel() instead. */ - virtual String getParameterLabel (int index) const; + JUCE_DEPRECATED (virtual String getParameterLabel (int index) const); /** This can be overridden to tell the host that particular parameters operate in the reverse direction. (Not all plugin formats or hosts will actually use this information). @@ -1092,7 +1092,7 @@ public: NOTE! This method will eventually be deprecated! It's recommended that you use AudioProcessorParameter::isOrientationInverted() instead. */ - virtual bool isParameterOrientationInverted (int index) const; + JUCE_DEPRECATED (virtual bool isParameterOrientationInverted (int index) const); /** The host will call this method to change the value of one of the processor's parameters. @@ -1110,7 +1110,7 @@ public: NOTE! This method will eventually be deprecated! It's recommended that you use AudioProcessorParameter::setValue() instead. */ - virtual void setParameter (int parameterIndex, float newValue); + JUCE_DEPRECATED (virtual void setParameter (int parameterIndex, float newValue)); /** Your processor can call this when it needs to change one of its parameters. @@ -1133,7 +1133,7 @@ public: NOTE! This method will eventually be deprecated! It's recommended that you use AudioProcessorParameter::isAutomatable() instead. */ - virtual bool isParameterAutomatable (int parameterIndex) const; + JUCE_DEPRECATED (virtual bool isParameterAutomatable (int parameterIndex) const); /** Should return true if this parameter is a "meta" parameter. A meta-parameter is a parameter that changes other params. It is used @@ -1143,7 +1143,7 @@ public: NOTE! This method will eventually be deprecated! It's recommended that you use AudioProcessorParameter::isMetaParameter() instead. */ - virtual bool isMetaParameter (int parameterIndex) const; + JUCE_DEPRECATED (virtual bool isMetaParameter (int parameterIndex) const); /** Should return the parameter's category. By default, this returns the "generic" category. @@ -1151,7 +1151,7 @@ public: NOTE! This method will eventually be deprecated! It's recommended that you use AudioProcessorParameter::getCategory() instead. */ - virtual AudioProcessorParameter::Category getParameterCategory (int parameterIndex) const; + JUCE_DEPRECATED (virtual AudioProcessorParameter::Category getParameterCategory (int parameterIndex) const); /** Sends a signal to the host to tell it that the user is about to start changing this parameter. @@ -1164,7 +1164,7 @@ public: NOTE! This method will eventually be deprecated! It's recommended that you use AudioProcessorParameter::beginChangeGesture() instead. */ - void beginParameterChangeGesture (int parameterIndex); + JUCE_DEPRECATED (void beginParameterChangeGesture (int parameterIndex)); /** Tells the host that the user has finished changing this parameter. @@ -1176,7 +1176,7 @@ public: NOTE! This method will eventually be deprecated! It's recommended that you use AudioProcessorParameter::endChangeGesture() instead. */ - void endParameterChangeGesture (int parameterIndex); + JUCE_DEPRECATED (void endParameterChangeGesture (int parameterIndex)); /** The processor can call this when something (apart from a parameter value) has changed. @@ -1626,8 +1626,6 @@ private: friend class AudioUnitPluginInstance; friend class LADSPAPluginInstance; - Atomic vst3IsPlaying { 0 }; - // This method is no longer used - you can delete it from your AudioProcessor classes. JUCE_DEPRECATED_WITH_BODY (virtual bool silenceInProducesSilenceOut() const, { return false; }) diff --git a/modules/juce_audio_processors/processors/juce_GenericAudioProcessorEditor.cpp b/modules/juce_audio_processors/processors/juce_GenericAudioProcessorEditor.cpp index 5d471375be..f0d737713d 100644 --- a/modules/juce_audio_processors/processors/juce_GenericAudioProcessorEditor.cpp +++ b/modules/juce_audio_processors/processors/juce_GenericAudioProcessorEditor.cpp @@ -27,166 +27,6 @@ namespace juce { -class ProcessorParameterPropertyComp : public PropertyComponent, - private AudioProcessorListener, - private Timer -{ -public: - ProcessorParameterPropertyComp (const String& name, AudioProcessor& p, int paramIndex) - : PropertyComponent (name), - owner (p), - index (paramIndex), - slider (p, paramIndex) - { - startTimer (100); - addAndMakeVisible (slider); - owner.addListener (this); - } - - ~ProcessorParameterPropertyComp() - { - owner.removeListener (this); - } - - void refresh() override - { - paramHasChanged = false; - - if (slider.getThumbBeingDragged() < 0) - slider.setValue (owner.getParameter (index), dontSendNotification); - - slider.updateText(); - } - - void audioProcessorChanged (AudioProcessor*) override {} - - void audioProcessorParameterChanged (AudioProcessor*, int parameterIndex, float) override - { - if (parameterIndex == index) - paramHasChanged = true; - } - - void timerCallback() override - { - if (paramHasChanged) - { - refresh(); - startTimerHz (50); - } - else - { - startTimer (jmin (1000 / 4, getTimerInterval() + 10)); - } - } - -private: - //============================================================================== - class ParamSlider : public Slider - { - public: - ParamSlider (AudioProcessor& p, int paramIndex) : owner (p), index (paramIndex) - { - auto steps = owner.getParameterNumSteps (index); - auto category = p.getParameterCategory (index); - bool isLevelMeter = (((category & 0xffff0000) >> 16) == 2); - - if (steps > 1 && steps < 0x7fffffff) - setRange (0.0, 1.0, 1.0 / (steps - 1.0)); - else - setRange (0.0, 1.0); - - setEnabled (! isLevelMeter); - setSliderStyle (Slider::LinearBar); - setTextBoxIsEditable (false); - setScrollWheelEnabled (true); - } - - void valueChanged() override - { - auto newVal = static_cast (getValue()); - - if (owner.getParameter (index) != newVal) - { - owner.setParameterNotifyingHost (index, newVal); - updateText(); - } - } - - void startedDragging() override - { - owner.beginParameterChangeGesture (index); - } - - void stoppedDragging() override - { - owner.endParameterChangeGesture (index); - } - - String getTextFromValue (double /*value*/) override - { - return (owner.getParameterText (index) + " " + owner.getParameterLabel (index)).trimEnd(); - } - - private: - //============================================================================== - AudioProcessor& owner; - const int index; - - JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ParamSlider) - }; - - AudioProcessor& owner; - const int index; - bool volatile paramHasChanged = false; - ParamSlider slider; - - JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ProcessorParameterPropertyComp) -}; - -struct LegacyParametersPanel : public Component -{ - LegacyParametersPanel (AudioProcessor* const processor) - { - addAndMakeVisible (panel); - - Array params; - - auto numParams = processor->getNumParameters(); - int totalHeight = 0; - - for (int i = 0; i < numParams; ++i) - { - String name (processor->getParameterName (i)); - - if (name.trim().isEmpty()) - name = "Unnamed"; - - auto* pc = new ProcessorParameterPropertyComp (name, *processor, i); - params.add (pc); - totalHeight += pc->getPreferredHeight(); - } - - panel.addProperties (params); - - setSize (400, jmax (25, totalHeight)); - } - - void paint (Graphics& g) override - { - g.fillAll (getLookAndFeel().findColour (ResizableWindow::backgroundColourId)); - } - - void resized() override - { - panel.setBounds (getLocalBounds()); - } - - PropertyPanel panel; - - JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (LegacyParametersPanel) -}; - -//============================================================================== class ParameterListener : private AudioProcessorParameter::Listener, private Timer { @@ -615,7 +455,7 @@ private: class ParametersPanel : public Component { public: - ParametersPanel (const OwnedArray& parameters) + ParametersPanel (const Array& parameters) { for (auto* param : parameters) if (param->isAutomatable()) @@ -647,25 +487,40 @@ private: }; //============================================================================== -GenericAudioProcessorEditor::GenericAudioProcessorEditor (AudioProcessor* const p) - : AudioProcessorEditor (p) +struct GenericAudioProcessorEditor::Pimpl { - jassert (p != nullptr); - setOpaque (true); + Pimpl (GenericAudioProcessorEditor& parent) + : owner (parent) + { + auto* p = parent.getAudioProcessor(); + jassert (p != nullptr); - auto& parameters = p->getParameters(); + juceParameters.update (*p, false); - if (parameters.size() == p->getNumParameters()) - view.setViewedComponent (new ParametersPanel (parameters)); - else - view.setViewedComponent (new LegacyParametersPanel (p)); + owner.setOpaque (true); - addAndMakeVisible (view); + view.setViewedComponent (new ParametersPanel (juceParameters.params)); + owner.addAndMakeVisible (view); - view.setScrollBarsShown (true, false); - setSize (view.getViewedComponent()->getWidth() + view.getVerticalScrollBar().getWidth(), - jmin (view.getViewedComponent()->getHeight(), 400)); -} + view.setScrollBarsShown (true, false); + owner.setSize (view.getViewedComponent()->getWidth() + view.getVerticalScrollBar().getWidth(), + jmin (view.getViewedComponent()->getHeight(), 400)); + } + + + //============================================================================== + GenericAudioProcessorEditor& owner; + LegacyAudioParametersWrapper juceParameters; + Viewport view; + + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Pimpl) +}; + + +//============================================================================== +GenericAudioProcessorEditor::GenericAudioProcessorEditor (AudioProcessor* const p) + : AudioProcessorEditor (p), pimpl (new Pimpl (*this)) +{} GenericAudioProcessorEditor::~GenericAudioProcessorEditor() {} @@ -676,7 +531,7 @@ void GenericAudioProcessorEditor::paint (Graphics& g) void GenericAudioProcessorEditor::resized() { - view.setBounds (getLocalBounds()); + pimpl->view.setBounds (getLocalBounds()); } } // namespace juce diff --git a/modules/juce_audio_processors/processors/juce_GenericAudioProcessorEditor.h b/modules/juce_audio_processors/processors/juce_GenericAudioProcessorEditor.h index 9945f20b5e..c79be1b5fe 100644 --- a/modules/juce_audio_processors/processors/juce_GenericAudioProcessorEditor.h +++ b/modules/juce_audio_processors/processors/juce_GenericAudioProcessorEditor.h @@ -52,7 +52,8 @@ public: private: //============================================================================== - Viewport view; + struct Pimpl; + ScopedPointer pimpl; JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (GenericAudioProcessorEditor) };