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:
parent
46d24cc1ab
commit
b1917085db
16 changed files with 134 additions and 53 deletions
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
|
|
|
|||
|
|
@ -914,7 +914,7 @@ public:
|
|||
#endif
|
||||
|
||||
//==============================================================================
|
||||
void audioProcessorChanged (AudioProcessor* processor) override
|
||||
void audioProcessorChanged (AudioProcessor* processor, const ChangeDetails&) override
|
||||
{
|
||||
ignoreUnused (processor);
|
||||
|
||||
|
|
|
|||
|
|
@ -790,7 +790,7 @@ public:
|
|||
ReleaseControl (index + 2);
|
||||
}
|
||||
|
||||
void audioProcessorChanged (AudioProcessor*) override
|
||||
void audioProcessorChanged (AudioProcessor*, const ChangeDetails&) override
|
||||
{
|
||||
// xxx is there an RTAS equivalent?
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
};
|
||||
|
|
|
|||
|
|
@ -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));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
||||
|
|
|
|||
|
|
@ -73,7 +73,7 @@ private:
|
|||
parameterValueHasChanged = 1;
|
||||
}
|
||||
|
||||
void audioProcessorChanged (AudioProcessor*) override {}
|
||||
void audioProcessorChanged (AudioProcessor*, const ChangeDetails&) override {}
|
||||
|
||||
//==============================================================================
|
||||
void timerCallback() override
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue