1
0
Fork 0
mirror of https://github.com/juce-framework/JUCE.git synced 2026-01-10 23:44:24 +00:00

AudioProcessor: Update listener callback with change details

This commit is contained in:
reuk 2021-02-16 12:53:06 +00:00
parent 46d24cc1ab
commit b1917085db
No known key found for this signature in database
GPG key ID: 9ADCD339CFC98A11
16 changed files with 134 additions and 53 deletions

View file

@ -4,6 +4,30 @@ JUCE breaking changes
Develop
=======
Change
------
AudioProcessorListener::audioProcessorChanged gained a new parameter describing
the nature of any change.
Possible Issues
---------------
Code using the old function signature will not build until updated to use
the new signature.
Workaround
----------
Listeners should add the new parameter to any overrides of
audioProcessorChanged.
Rationale
---------
The new function signature means that wrappers can be smarter about the
requests that they make to hosts whenever some aspect of the processor changes.
In particular, plugin wrappers can now distinguish between changes to latency,
parameter attributes, and the current program. This means that hosts will no
longer assume parameters have changed when `setLatencySamples` is called.
Change
------
CharacterFunctions::readDoubleValue now returns values consistent with other

View file

@ -60,7 +60,7 @@ public:
//==============================================================================
void audioProcessorParameterChanged (AudioProcessor*, int, float) override {}
void audioProcessorChanged (AudioProcessor*) override { changed(); }
void audioProcessorChanged (AudioProcessor*, const ChangeDetails&) override { changed(); }
//==============================================================================
std::unique_ptr<XmlElement> createXml() const;

View file

@ -287,7 +287,7 @@ private:
}
void refresh() override {}
void audioProcessorChanged (AudioProcessor*) override {}
void audioProcessorChanged (AudioProcessor*, const ChangeDetails&) override {}
void audioProcessorParameterChanged (AudioProcessor*, int, float) override {}
AudioProcessor& owner;

View file

@ -1085,24 +1085,28 @@ namespace AAXClasses
SetParameterNormalizedValue (paramID, (double) newValue);
}
void audioProcessorChanged (AudioProcessor* processor) override
void audioProcessorChanged (AudioProcessor* processor, const ChangeDetails& details) override
{
++mNumPlugInChanges;
auto numParameters = juceParameters.getNumParameters();
for (int i = 0; i < numParameters; ++i)
if (details.parameterInfoChanged)
{
if (auto* p = mParameterManager.GetParameterByID (getAAXParamIDFromJuceIndex (i)))
{
auto newName = juceParameters.getParamForIndex (i)->getName (31);
auto numParameters = juceParameters.getNumParameters();
if (p->Name() != newName.toRawUTF8())
p->SetName (AAX_CString (newName.toRawUTF8()));
for (int i = 0; i < numParameters; ++i)
{
if (auto* p = mParameterManager.GetParameterByID (getAAXParamIDFromJuceIndex (i)))
{
auto newName = juceParameters.getParamForIndex (i)->getName (31);
if (p->Name() != newName.toRawUTF8())
p->SetName (AAX_CString (newName.toRawUTF8()));
}
}
}
check (Controller()->SetSignalLatency (processor->getLatencySamples()));
if (details.latencyChanged)
check (Controller()->SetSignalLatency (processor->getLatencySamples()));
}
void audioProcessorParameterChangeGestureBegin (AudioProcessor*, int parameterIndex) override

View file

@ -1166,16 +1166,24 @@ public:
sendAUEvent (kAudioUnitEvent_EndParameterChangeGesture, index);
}
void audioProcessorChanged (AudioProcessor*) override
void audioProcessorChanged (AudioProcessor*, const ChangeDetails& details) override
{
PropertyChanged (kAudioUnitProperty_Latency, kAudioUnitScope_Global, 0);
PropertyChanged (kAudioUnitProperty_ParameterList, kAudioUnitScope_Global, 0);
PropertyChanged (kAudioUnitProperty_ParameterInfo, kAudioUnitScope_Global, 0);
PropertyChanged (kAudioUnitProperty_ClassInfo, kAudioUnitScope_Global, 0);
if (details.latencyChanged)
PropertyChanged (kAudioUnitProperty_Latency, kAudioUnitScope_Global, 0);
refreshCurrentPreset();
if (details.parameterInfoChanged)
{
PropertyChanged (kAudioUnitProperty_ParameterList, kAudioUnitScope_Global, 0);
PropertyChanged (kAudioUnitProperty_ParameterInfo, kAudioUnitScope_Global, 0);
}
PropertyChanged (kAudioUnitProperty_PresentPreset, kAudioUnitScope_Global, 0);
PropertyChanged (kAudioUnitProperty_ClassInfo, kAudioUnitScope_Global, 0);
if (details.programChanged)
{
refreshCurrentPreset();
PropertyChanged (kAudioUnitProperty_PresentPreset, kAudioUnitScope_Global, 0);
}
}
//==============================================================================

View file

@ -914,7 +914,7 @@ public:
#endif
//==============================================================================
void audioProcessorChanged (AudioProcessor* processor) override
void audioProcessorChanged (AudioProcessor* processor, const ChangeDetails&) override
{
ignoreUnused (processor);

View file

@ -790,7 +790,7 @@ public:
ReleaseControl (index + 2);
}
void audioProcessorChanged (AudioProcessor*) override
void audioProcessorChanged (AudioProcessor*, const ChangeDetails&) override
{
// xxx is there an RTAS equivalent?
}

View file

@ -254,7 +254,6 @@ struct AbletonLiveHostSpecific
class JuceVSTWrapper : public AudioProcessorListener,
public AudioPlayHead,
private Timer,
private AsyncUpdater,
private AudioProcessorParameter::Listener
{
private:
@ -802,19 +801,9 @@ public:
void parameterGestureChanged (int, bool) override {}
void audioProcessorChanged (AudioProcessor*) override
void audioProcessorChanged (AudioProcessor*, const ChangeDetails& details) override
{
vstEffect.initialDelay = processor->getLatencySamples();
triggerAsyncUpdate();
}
void handleAsyncUpdate() override
{
if (hostCallback != nullptr)
{
hostCallback (&vstEffect, Vst2::audioMasterUpdateDisplay, 0, 0, nullptr, 0);
hostCallback (&vstEffect, Vst2::audioMasterIOChanged, 0, 0, nullptr, 0);
}
hostChangeUpdater.update (details);
}
bool getPinProperties (Vst2::VstPinProperties& properties, bool direction, int index) const
@ -1398,6 +1387,33 @@ public:
//==============================================================================
private:
struct HostChangeUpdater : private AsyncUpdater
{
explicit HostChangeUpdater (JuceVSTWrapper& o) : owner (o) {}
~HostChangeUpdater() override { cancelPendingUpdate(); }
void update (const ChangeDetails& details)
{
if (details.latencyChanged)
owner.vstEffect.initialDelay = owner.processor->getLatencySamples();
if (details.parameterInfoChanged || details.programChanged)
triggerAsyncUpdate();
}
private:
void handleAsyncUpdate() override
{
if (auto* callback = owner.hostCallback)
{
callback (&owner.vstEffect, Vst2::audioMasterUpdateDisplay, 0, 0, nullptr, 0);
callback (&owner.vstEffect, Vst2::audioMasterIOChanged, 0, 0, nullptr, 0);
}
}
JuceVSTWrapper& owner;
};
static JuceVSTWrapper* getWrapper (Vst2::AEffect* v) noexcept { return static_cast<JuceVSTWrapper*> (v->object); }
bool isProcessLevelOffline()
@ -2121,6 +2137,8 @@ private:
ThreadLocalValue<bool> inParameterChangedCallback;
HostChangeUpdater hostChangeUpdater { *this };
//==============================================================================
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (JuceVSTWrapper)
};

View file

@ -960,18 +960,21 @@ public:
paramChanged (audioProcessor->getVSTParamIDForIndex (index), newValue);
}
void audioProcessorChanged (AudioProcessor*) override
void audioProcessorChanged (AudioProcessor*, const ChangeDetails& details) override
{
int32 flags = 0;
for (int32 i = 0; i < parameters.getParameterCount(); ++i)
if (auto* param = dynamic_cast<Param*> (parameters.getParameterByIndex (i)))
if (param->updateParameterInfo() && (flags & Vst::kParamTitlesChanged) == 0)
flags |= Vst::kParamTitlesChanged;
if (details.parameterInfoChanged)
{
for (int32 i = 0; i < parameters.getParameterCount(); ++i)
if (auto* param = dynamic_cast<Param*> (parameters.getParameterByIndex (i)))
if (param->updateParameterInfo() && (flags & Vst::kParamTitlesChanged) == 0)
flags |= Vst::kParamTitlesChanged;
}
if (auto* pluginInstance = getPluginInstance())
{
if (audioProcessor->getProgramParameter() != nullptr)
if (details.programChanged && audioProcessor->getProgramParameter() != nullptr)
{
auto currentProgram = pluginInstance->getCurrentProgram();
auto paramValue = roundToInt (EditController::normalizedParamToPlain (JuceAudioProcessor::paramPreset,
@ -990,7 +993,7 @@ public:
auto latencySamples = pluginInstance->getLatencySamples();
if (latencySamples != lastLatencySamples)
if (details.latencyChanged && latencySamples != lastLatencySamples)
{
flags |= Vst::kLatencyChanged;
lastLatencySamples = latencySamples;
@ -1116,7 +1119,7 @@ private:
initialiseMidiControllerMappings();
#endif
audioProcessorChanged (pluginInstance);
audioProcessorChanged (pluginInstance, ChangeDetails().withParameterInfoChanged (true));
}
}

View file

@ -1822,12 +1822,12 @@ private:
default:
if (event.mArgument.mProperty.mPropertyID == kAudioUnitProperty_ParameterList)
{
updateHostDisplay();
updateHostDisplay (AudioProcessorListener::ChangeDetails().withParameterInfoChanged (true));
}
else if (event.mArgument.mProperty.mPropertyID == kAudioUnitProperty_PresentPreset)
{
sendAllParametersChangedEvents();
updateHostDisplay();
updateHostDisplay (AudioProcessorListener::ChangeDetails().withProgramChanged (true));
}
else if (event.mArgument.mProperty.mPropertyID == kAudioUnitProperty_Latency)
{

View file

@ -3179,7 +3179,9 @@ tresult VST3HostContext::restartComponent (Steinberg::int32 flags)
if (plugin->processor != nullptr)
plugin->setLatencySamples (jmax (0, (int) plugin->processor->getLatencySamples()));
plugin->updateHostDisplay();
plugin->updateHostDisplay (AudioProcessorListener::ChangeDetails().withProgramChanged (true)
.withParameterInfoChanged (true));
return kResultTrue;
}

View file

@ -1602,8 +1602,8 @@ struct VSTPluginInstance : public AudioPluginInstance,
void handleAsyncUpdate() override
{
// indicates that something about the plugin has changed..
updateHostDisplay();
updateHostDisplay (AudioProcessorListener::ChangeDetails().withProgramChanged (true)
.withParameterInfoChanged (true));
}
pointer_sized_int handleCallback (int32 opcode, int32 index, pointer_sized_int value, void* ptr, float opt)

View file

@ -410,7 +410,7 @@ void AudioProcessor::setLatencySamples (int newLatency)
if (latencySamples != newLatency)
{
latencySamples = newLatency;
updateHostDisplay();
updateHostDisplay (AudioProcessorListener::ChangeDetails().withLatencyChanged (true));
}
}
@ -421,11 +421,11 @@ AudioProcessorListener* AudioProcessor::getListenerLocked (int index) const noex
return listeners[index];
}
void AudioProcessor::updateHostDisplay()
void AudioProcessor::updateHostDisplay (const AudioProcessorListener::ChangeDetails& details)
{
for (int i = listeners.size(); --i >= 0;)
if (auto l = getListenerLocked (i))
l->audioProcessorChanged (this);
l->audioProcessorChanged (this, details);
}
void AudioProcessor::checkForDuplicateParamID (AudioProcessorParameter* param)

View file

@ -991,7 +991,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();
void updateHostDisplay (const AudioProcessorListener::ChangeDetails& details = {});
//==============================================================================
/** Adds a parameter to the AudioProcessor.

View file

@ -57,6 +57,28 @@ public:
int parameterIndex,
float newValue) = 0;
/** Provides details about aspects of an AudioProcessor which have changed.
*/
struct JUCE_API ChangeDetails
{
bool latencyChanged = false;
bool parameterInfoChanged = false;
bool programChanged = 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); }
private:
template <typename Member, typename Value>
ChangeDetails with (Member&& member, Value&& value) const noexcept
{
auto copy = *this;
copy.*member = std::forward<Value> (value);
return copy;
}
};
/** Called to indicate that something else in the plugin has changed, like its
program, number of parameters, etc.
@ -67,7 +89,7 @@ public:
to trigger an AsyncUpdater or ChangeBroadcaster which you can respond to later on the
message thread.
*/
virtual void audioProcessorChanged (AudioProcessor* processor) = 0;
virtual void audioProcessorChanged (AudioProcessor* processor, const ChangeDetails& details) = 0;
/** Indicates that a parameter change gesture has started.

View file

@ -73,7 +73,7 @@ private:
parameterValueHasChanged = 1;
}
void audioProcessorChanged (AudioProcessor*) override {}
void audioProcessorChanged (AudioProcessor*, const ChangeDetails&) override {}
//==============================================================================
void timerCallback() override