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 0d2d29103b..4cc25d97a0 100644 --- a/modules/juce_audio_plugin_client/VST3/juce_VST3_Wrapper.cpp +++ b/modules/juce_audio_plugin_client/VST3/juce_VST3_Wrapper.cpp @@ -1261,6 +1261,9 @@ public: } } + if (details.nonParameterStateChanged) + flags |= pluginShouldBeMarkedDirtyFlag; + if (! inSetupProcessing) componentRestarter.restart (flags); } @@ -1274,6 +1277,8 @@ public: return nullptr; } + static constexpr auto pluginShouldBeMarkedDirtyFlag = 1 << 16; + private: friend class JuceVST3Component; friend struct Param; @@ -1295,6 +1300,11 @@ private: void restartComponentOnMessageThread (int32 flags) override { + if ((flags & pluginShouldBeMarkedDirtyFlag) != 0) + setDirty (true); + + flags &= ~pluginShouldBeMarkedDirtyFlag; + if (auto* handler = componentHandler) handler->restartComponent (flags); } diff --git a/modules/juce_audio_processors/format_types/juce_VST3PluginFormat.cpp b/modules/juce_audio_processors/format_types/juce_VST3PluginFormat.cpp index e52ade4079..225de4b1b3 100644 --- a/modules/juce_audio_processors/format_types/juce_VST3PluginFormat.cpp +++ b/modules/juce_audio_processors/format_types/juce_VST3PluginFormat.cpp @@ -349,13 +349,9 @@ struct VST3HostContext : public Vst::IComponentHandler, // From VST V3.0.0 tresult PLUGIN_API endEdit (Vst::ParamID paramID) override; tresult PLUGIN_API restartComponent (Steinberg::int32 flags) override; + tresult PLUGIN_API setDirty (TBool) override; //============================================================================== - tresult PLUGIN_API setDirty (TBool) override - { - return kResultFalse; - } - tresult PLUGIN_API requestOpenEditor (FIDString name) override { ignoreUnused (name); @@ -3508,6 +3504,14 @@ tresult VST3HostContext::restartComponent (Steinberg::int32 flags) return kResultTrue; } +tresult PLUGIN_API VST3HostContext::setDirty (TBool needsSave) +{ + if (needsSave) + plugin->updateHostDisplay (AudioPluginInstance::ChangeDetails{}.withNonParameterStateChanged (true)); + + return kResultOk; +} + void VST3HostContext::restartComponentOnMessageThread (int32 flags) { if (plugin == nullptr) diff --git a/modules/juce_audio_processors/processors/juce_AudioProcessor.h b/modules/juce_audio_processors/processors/juce_AudioProcessor.h index edb9ccf06d..3cb5b78e8a 100644 --- a/modules/juce_audio_processors/processors/juce_AudioProcessor.h +++ b/modules/juce_audio_processors/processors/juce_AudioProcessor.h @@ -993,7 +993,7 @@ public: It sends a hint to the host that something like the program, number of parameters, etc, has changed, and that it should update itself. */ - void updateHostDisplay (const ChangeDetails& details = ChangeDetails::getAllChanged()); + void updateHostDisplay (const ChangeDetails& details = ChangeDetails::getDefaultFlags()); //============================================================================== /** Adds a parameter to the AudioProcessor. diff --git a/modules/juce_audio_processors/processors/juce_AudioProcessorListener.h b/modules/juce_audio_processors/processors/juce_AudioProcessorListener.h index ad9070dffd..aba6310412 100644 --- a/modules/juce_audio_processors/processors/juce_AudioProcessorListener.h +++ b/modules/juce_audio_processors/processors/juce_AudioProcessorListener.h @@ -61,21 +61,73 @@ public: */ struct JUCE_API ChangeDetails { - bool latencyChanged = false; - bool parameterInfoChanged = false; - bool programChanged = false; + /** @see withLatencyChanged */ + bool latencyChanged = false; + /** @see withParameterInfoChanged */ + bool parameterInfoChanged = false; + /** @see withProgramChanged */ + bool programChanged = false; + /** @see withNonParameterStateChanged */ + bool nonParameterStateChanged = false; - ChangeDetails withLatencyChanged (bool b) const noexcept { return with (&ChangeDetails::latencyChanged, b); } - ChangeDetails withParameterInfoChanged (bool b) const noexcept { return with (&ChangeDetails::parameterInfoChanged, b); } - ChangeDetails withProgramChanged (bool b) const noexcept { return with (&ChangeDetails::programChanged, b); } + /** Indicates that the AudioProcessor's latency has changed. - static ChangeDetails getAllChanged() + Most of the time, you won't need to use this function directly. + AudioProcessor::setLatencySamples() will automatically call + AudioProcessor::updateHostDisplay(), indicating that the latency has changed. + + @see latencyChanged + */ + ChangeDetails withLatencyChanged (bool b) const noexcept { return with (&ChangeDetails::latencyChanged, b); } + + /** Indicates that some attributes of the AudioProcessor's parameters have changed. + + When this flag is set, the host should rescan the AudioProcessor's parameters, and + update its controls to match. This is often used to update the names of a plugin's + parameters in the host. + + @see parameterInfoChanged + */ + ChangeDetails withParameterInfoChanged (bool b) const noexcept { return with (&ChangeDetails::parameterInfoChanged, b); } + + /** Indicates that the loaded program has changed. + + When this flag is set, the host should call AudioProcessor::getCurrentProgram() and + update any preset list views to display the program that is currently in use. + + @see programChanged + */ + ChangeDetails withProgramChanged (bool b) const noexcept { return with (&ChangeDetails::programChanged, b); } + + /** Indicates that the plugin state has changed (but not its parameters!). + + An AudioProcessor can call updateHostDisplay with this flag set to notify the host that + its state has changed in a way that requires re-saving. + + If a host receives a call to audioProcessorChanged with this flag set, it should offer + to save the plugin state before taking any actions that might irrevocably destroy the + current plugin state, such as closing the project. + + @see nonParameterStateChanged + */ + ChangeDetails withNonParameterStateChanged (bool b) const noexcept { return with (&ChangeDetails::nonParameterStateChanged, b); } + + /** Returns the default set of flags that will be used when + AudioProcessor::updateHostDisplay() is called with no arguments. + */ + static ChangeDetails getDefaultFlags() { return ChangeDetails{}.withLatencyChanged (true) .withParameterInfoChanged (true) .withProgramChanged (true); } + [[deprecated ("The naming of this function is misleading. Use getDefaultFlags instead.")]] + static ChangeDetails getAllChanged() + { + return getDefaultFlags(); + } + private: template ChangeDetails with (Member&& member, Value&& value) const noexcept