diff --git a/BREAKING-CHANGES.txt b/BREAKING-CHANGES.txt index 74c6edbdea..97cdf71203 100644 --- a/BREAKING-CHANGES.txt +++ b/BREAKING-CHANGES.txt @@ -1,6 +1,36 @@ JUCE breaking changes ===================== +develop +======= + +Change +------ +Functions on AudioPluginInstance that can add parameters have been made +private. + +Possible Issues +--------------- +Code implementing custom plugin formats may stop building if it calls these +functions. + +Workaround +---------- +When implementing custom plugin formats, ensure that the plugin parameters +derive from AudioPluginInstance::HostedParameter and then use +addHostedParameter, addHostedParameterGroup or setHostedParameterTree to add +the parameters to the plugin instance. + +Rationale +--------- +In a plugin host, it is very important to be able to uniquely identify +parameters across different versions of the same plugin. To make this possible, +we needed to introduce a way of retrieving a unique ID for each parameter, +which is now possible using the HostedParameter class. However, we also needed +to enforce that all AudioPluginInstances can only have parameters which are of +the type HostedParameter, which required hiding the old functions. + + Version 6.1.0 ============= diff --git a/modules/juce_audio_processors/format_types/juce_AudioUnitPluginFormat.mm b/modules/juce_audio_processors/format_types/juce_AudioUnitPluginFormat.mm index ca6d921087..6e3fd96f33 100644 --- a/modules/juce_audio_processors/format_types/juce_AudioUnitPluginFormat.mm +++ b/modules/juce_audio_processors/format_types/juce_AudioUnitPluginFormat.mm @@ -330,7 +330,7 @@ class AudioUnitPluginWindowCarbon; class AudioUnitPluginWindowCocoa; //============================================================================== -class AudioUnitPluginInstance : public AudioPluginInstance +class AudioUnitPluginInstance final : public AudioPluginInstance { public: struct AUInstanceParameter final : public Parameter @@ -488,6 +488,11 @@ public: return auValueStrings; } + String getParameterID() const override + { + return String (paramID); + } + void sendParameterChangeEvent() { #if JUCE_MAC @@ -514,6 +519,9 @@ public: return minValue + (range * normalisedValue); } + UInt32 getRawParamID() const { return paramID; } + + private: AudioUnitPluginInstance& pluginInstance; const UInt32 paramID; const String name; @@ -1488,7 +1496,7 @@ public: } } - setParameterTree (std::move (newParameterTree)); + setHostedParameterTree (std::move (newParameterTree)); UInt32 propertySize = 0; Boolean writable = false; @@ -1554,7 +1562,7 @@ private: }; //============================================================================== - struct AUBypassParameter : Parameter + struct AUBypassParameter final : public Parameter { AUBypassParameter (AudioUnitPluginInstance& effectToUse) : parent (effectToUse), currentValue (getCurrentHostValue()) @@ -1638,6 +1646,8 @@ private: StringArray getAllValueStrings() const override { return values; } String getLabel() const override { return {}; } + String getParameterID() const override { return {}; } + AudioUnitPluginInstance& parent; const StringArray auOnStrings { TRANS("on"), TRANS("yes"), TRANS("true") }; const StringArray auOffStrings { TRANS("off"), TRANS("no"), TRANS("false") }; @@ -1725,7 +1735,7 @@ private: AudioUnitEvent event; event.mArgument.mParameter.mAudioUnit = audioUnit; - event.mArgument.mParameter.mParameterID = static_cast (param)->paramID; + event.mArgument.mParameter.mParameterID = static_cast (param)->getRawParamID(); event.mArgument.mParameter.mScope = kAudioUnitScope_Global; event.mArgument.mParameter.mElement = 0; diff --git a/modules/juce_audio_processors/format_types/juce_LADSPAPluginFormat.cpp b/modules/juce_audio_processors/format_types/juce_LADSPAPluginFormat.cpp index 536e59cb5f..f06e0781dd 100644 --- a/modules/juce_audio_processors/format_types/juce_LADSPAPluginFormat.cpp +++ b/modules/juce_audio_processors/format_types/juce_LADSPAPluginFormat.cpp @@ -113,7 +113,7 @@ private: }; //============================================================================== -class LADSPAPluginInstance : public AudioPluginInstance +class LADSPAPluginInstance final : public AudioPluginInstance { public: LADSPAPluginInstance (const LADSPAModuleHandle::Ptr& m) @@ -197,7 +197,7 @@ public: } } - setParameterTree (std::move (newTree)); + setHostedParameterTree (std::move (newTree)); for (auto* param : getParameters()) if (auto* ladspaParam = dynamic_cast (param)) @@ -516,6 +516,11 @@ private: bool isAutomatable() const override { return automatable; } + String getParameterID() const override + { + return String (paramID); + } + static float scaledValue (float low, float high, float alpha, bool useLog) noexcept { if (useLog && low > 0 && high > 0) diff --git a/modules/juce_audio_processors/format_types/juce_VST3PluginFormat.cpp b/modules/juce_audio_processors/format_types/juce_VST3PluginFormat.cpp index 9dc44ec557..2db66abfa5 100644 --- a/modules/juce_audio_processors/format_types/juce_VST3PluginFormat.cpp +++ b/modules/juce_audio_processors/format_types/juce_VST3PluginFormat.cpp @@ -2037,7 +2037,7 @@ private: }; //============================================================================== -class VST3PluginInstance : public AudioPluginInstance +class VST3PluginInstance final : public AudioPluginInstance { public: //============================================================================== @@ -2153,6 +2153,11 @@ public: return {}; } + String getParameterID() const override + { + return String (paramID); + } + Steinberg::Vst::ParamID getParamID() const noexcept { return paramID; } private: @@ -3141,7 +3146,7 @@ private: group->addChild (std::unique_ptr (param)); } - setParameterTree (std::move (newParameterTree)); + setHostedParameterTree (std::move (newParameterTree)); idToParamMap = [this] { diff --git a/modules/juce_audio_processors/format_types/juce_VSTPluginFormat.cpp b/modules/juce_audio_processors/format_types/juce_VSTPluginFormat.cpp index 7771cbb894..a9c24afa7a 100644 --- a/modules/juce_audio_processors/format_types/juce_VSTPluginFormat.cpp +++ b/modules/juce_audio_processors/format_types/juce_VSTPluginFormat.cpp @@ -828,9 +828,9 @@ static const int defaultVSTBlockSizeValue = 512; JUCE_BEGIN_IGNORE_WARNINGS_MSVC (4996) //============================================================================== -struct VSTPluginInstance : public AudioPluginInstance, - private Timer, - private AsyncUpdater +struct VSTPluginInstance final : public AudioPluginInstance, + private Timer, + private AsyncUpdater { struct VSTParameter final : public Parameter { @@ -972,6 +972,11 @@ struct VSTPluginInstance : public AudioPluginInstance, return vstValueStrings; } + String getParameterID() const override + { + return String (getParameterIndex()); + } + VSTPluginInstance& pluginInstance; const String name; @@ -1077,7 +1082,7 @@ struct VSTPluginInstance : public AudioPluginInstance, isBoolSwitch, parameterValueStrings, valueType)); } - setParameterTree (std::move (newParameterTree)); + setHostedParameterTree (std::move (newParameterTree)); } ~VSTPluginInstance() override @@ -2005,7 +2010,7 @@ struct VSTPluginInstance : public AudioPluginInstance, private: //============================================================================== - struct VST2BypassParameter : Parameter + struct VST2BypassParameter final : public Parameter { VST2BypassParameter (VSTPluginInstance& effectToUse) : parent (effectToUse), @@ -2048,6 +2053,7 @@ private: int getNumSteps() const override { return 2; } StringArray getAllValueStrings() const override { return values; } String getLabel() const override { return {}; } + String getParameterID() const override { return {}; } VSTPluginInstance& parent; bool currentValue = false; diff --git a/modules/juce_audio_processors/processors/juce_AudioPluginInstance.cpp b/modules/juce_audio_processors/processors/juce_AudioPluginInstance.cpp index 6153487eff..c3112c103f 100644 --- a/modules/juce_audio_processors/processors/juce_AudioPluginInstance.cpp +++ b/modules/juce_audio_processors/processors/juce_AudioPluginInstance.cpp @@ -221,7 +221,7 @@ AudioPluginInstance::Parameter::Parameter() offStrings.add (TRANS("false")); } -AudioPluginInstance::Parameter::~Parameter() {} +AudioPluginInstance::Parameter::~Parameter() = default; String AudioPluginInstance::Parameter::getText (float value, int maximumStringLength) const { @@ -249,4 +249,44 @@ float AudioPluginInstance::Parameter::getValueForText (const String& text) const return floatValue; } +void AudioPluginInstance::addHostedParameter (std::unique_ptr param) +{ + addParameter (param.release()); +} + +void AudioPluginInstance::addHostedParameterGroup (std::unique_ptr group) +{ + #if JUCE_DEBUG + // All parameters *must* be HostedParameters, otherwise getHostedParameter will return + // garbage and your host will crash and burn + for (auto* param : group->getParameters (true)) + { + jassert (dynamic_cast (param) != nullptr); + } + #endif + + addParameterGroup (std::move (group)); +} + +void AudioPluginInstance::setHostedParameterTree (AudioProcessorParameterGroup group) +{ + #if JUCE_DEBUG + // All parameters *must* be HostedParameters, otherwise getHostedParameter will return + // garbage and your host will crash and burn + for (auto* param : group.getParameters (true)) + { + jassert (dynamic_cast (param) != nullptr); + } + #endif + + setParameterTree (std::move (group)); +} + +AudioPluginInstance::HostedParameter* AudioPluginInstance::getHostedParameter (int index) const +{ + // It's important that all AudioPluginInstance implementations + // only ever own HostedParameters! + return static_cast (getParameters()[index]); +} + } // namespace juce diff --git a/modules/juce_audio_processors/processors/juce_AudioPluginInstance.h b/modules/juce_audio_processors/processors/juce_AudioPluginInstance.h index 8a5a559990..260a604ac4 100644 --- a/modules/juce_audio_processors/processors/juce_AudioPluginInstance.h +++ b/modules/juce_audio_processors/processors/juce_AudioPluginInstance.h @@ -76,6 +76,55 @@ public: */ virtual void getExtensions (ExtensionsVisitor&) const; + /** + A parameter with functions which are useful for plugin hosts. + */ + struct HostedParameter : public AudioProcessorParameter + { + /** Returns an ID which is unique to this parameter. + + Parameter indices are unstable across plugin versions, which means that the + parameter found at a particular index in one version of a plugin might move + to a different index in the subsequent version. + + Unlike the parameter index, the ID returned by this function should be + somewhat stable (depending on the format of the plugin), so it is more + suitable for storing/recalling automation data. + */ + virtual String getParameterID() const = 0; + }; + + /** Adds a parameter to this instance. + + @see AudioProcessor::addParameter() + */ + void addHostedParameter (std::unique_ptr); + + /** Adds multiple parameters to this instance. + + In debug mode, this will also check that all added parameters derive from + HostedParameter. + + @see AudioProcessor::addParameterGroup() + */ + void addHostedParameterGroup (std::unique_ptr); + + /** Adds multiple parameters to this instance. + + In debug mode, this will also check that all added parameters derive from + HostedParameter. + + @see AudioProcessor::setParameterTree() + */ + void setHostedParameterTree (AudioProcessorParameterGroup); + + /** Gets the parameter at a particular index. + + If you want to find lots of parameters by their IDs, you should probably build and + use a map by looping through all parameters. + */ + HostedParameter* getHostedParameter (int index) const; + /** Use the new typesafe visitor-based interface rather than this function. Returns a pointer to some kind of platform-specific data about the plugin. @@ -110,14 +159,16 @@ public: protected: //============================================================================== /** Structure used to describe plugin parameters */ - struct Parameter : public AudioProcessorParameter + struct Parameter : public HostedParameter { + public: Parameter(); ~Parameter() override; String getText (float value, int maximumStringLength) const override; float getValueForText (const String& text) const override; + private: StringArray onStrings, offStrings; }; @@ -127,6 +178,12 @@ protected: AudioPluginInstance (const short channelLayoutList[numLayouts][2]) : AudioProcessor (channelLayoutList) {} private: + // It's not safe to add a plain AudioProcessorParameter to an AudioPluginInstance. + // Instead, all parameters must be HostedParameters. + using AudioProcessor::addParameter; + using AudioProcessor::addParameterGroup; + using AudioProcessor::setParameterTree; + void assertOnceOnDeprecatedMethodUse() const noexcept; static bool deprecationAssertiontriggered;