Browse Source

Added an AudioProcessorParameter listener class

tags/2021-05-28
Tom Poole 7 years ago
parent
commit
d3d1eeb770
3 changed files with 197 additions and 37 deletions
  1. +127
    -35
      modules/juce_audio_processors/processors/juce_AudioProcessor.cpp
  2. +2
    -2
      modules/juce_audio_processors/processors/juce_AudioProcessor.h
  3. +68
    -0
      modules/juce_audio_processors/processors/juce_AudioProcessorParameter.h

+ 127
- 35
modules/juce_audio_processors/processors/juce_AudioProcessor.cpp View File

@@ -416,8 +416,15 @@ void AudioProcessor::setLatencySamples (const int newLatency)
void AudioProcessor::setParameterNotifyingHost (int parameterIndex, float newValue) void AudioProcessor::setParameterNotifyingHost (int parameterIndex, float newValue)
{ {
setParameter (parameterIndex, newValue);
sendParamChangeMessageToListeners (parameterIndex, newValue);
if (auto* param = getParameters()[parameterIndex])
{
param->setValueNotifyingHost (newValue);
}
else
{
setParameter (parameterIndex, newValue);
sendParamChangeMessageToListeners (parameterIndex, newValue);
}
} }
AudioProcessorListener* AudioProcessor::getListenerLocked (int index) const noexcept AudioProcessorListener* AudioProcessor::getListenerLocked (int index) const noexcept
@@ -428,58 +435,79 @@ AudioProcessorListener* AudioProcessor::getListenerLocked (int index) const noex
void AudioProcessor::sendParamChangeMessageToListeners (int parameterIndex, float newValue) void AudioProcessor::sendParamChangeMessageToListeners (int parameterIndex, float newValue)
{ {
if (isPositiveAndBelow (parameterIndex, getNumParameters()))
if (auto* param = getParameters()[parameterIndex])
{ {
for (int i = listeners.size(); --i >= 0;)
if (auto* l = getListenerLocked (i))
l->audioProcessorParameterChanged (this, parameterIndex, newValue);
param->sendValueChangedMessageToListeners (newValue);
} }
else else
{ {
jassertfalse; // called with an out-of-range parameter index!
if (isPositiveAndBelow (parameterIndex, getNumParameters()))
{
for (int i = listeners.size(); --i >= 0;)
if (auto* l = getListenerLocked (i))
l->audioProcessorParameterChanged (this, parameterIndex, newValue);
}
else
{
jassertfalse; // called with an out-of-range parameter index!
}
} }
} }
void AudioProcessor::beginParameterChangeGesture (int parameterIndex) void AudioProcessor::beginParameterChangeGesture (int parameterIndex)
{ {
if (isPositiveAndBelow (parameterIndex, getNumParameters()))
if (auto* param = getParameters()[parameterIndex])
{ {
#if JUCE_DEBUG && ! JUCE_DISABLE_AUDIOPROCESSOR_BEGIN_END_GESTURE_CHECKING
// This means you've called beginParameterChangeGesture twice in succession without a matching
// call to endParameterChangeGesture. That might be fine in most hosts, but better to avoid doing it.
jassert (! changingParams[parameterIndex]);
changingParams.setBit (parameterIndex);
#endif
for (int i = listeners.size(); --i >= 0;)
if (auto* l = getListenerLocked (i))
l->audioProcessorParameterChangeGestureBegin (this, parameterIndex);
param->beginChangeGesture();
} }
else else
{ {
jassertfalse; // called with an out-of-range parameter index!
if (isPositiveAndBelow (parameterIndex, getNumParameters()))
{
#if JUCE_DEBUG && ! JUCE_DISABLE_AUDIOPROCESSOR_BEGIN_END_GESTURE_CHECKING
// This means you've called beginParameterChangeGesture twice in succession without a matching
// call to endParameterChangeGesture. That might be fine in most hosts, but better to avoid doing it.
jassert (! changingParams[parameterIndex]);
changingParams.setBit (parameterIndex);
#endif
for (int i = listeners.size(); --i >= 0;)
if (auto* l = getListenerLocked (i))
l->audioProcessorParameterChangeGestureBegin (this, parameterIndex);
}
else
{
jassertfalse; // called with an out-of-range parameter index!
}
} }
} }
void AudioProcessor::endParameterChangeGesture (int parameterIndex) void AudioProcessor::endParameterChangeGesture (int parameterIndex)
{ {
if (isPositiveAndBelow (parameterIndex, getNumParameters()))
if (auto* param = getParameters()[parameterIndex])
{ {
#if JUCE_DEBUG && ! JUCE_DISABLE_AUDIOPROCESSOR_BEGIN_END_GESTURE_CHECKING
// This means you've called endParameterChangeGesture without having previously called
// beginParameterChangeGesture. That might be fine in most hosts, but better to keep the
// calls matched correctly.
jassert (changingParams[parameterIndex]);
changingParams.clearBit (parameterIndex);
#endif
for (int i = listeners.size(); --i >= 0;)
if (auto* l = getListenerLocked (i))
l->audioProcessorParameterChangeGestureEnd (this, parameterIndex);
param->endChangeGesture();
} }
else else
{ {
jassertfalse; // called with an out-of-range parameter index!
if (isPositiveAndBelow (parameterIndex, getNumParameters()))
{
#if JUCE_DEBUG && ! JUCE_DISABLE_AUDIOPROCESSOR_BEGIN_END_GESTURE_CHECKING
// This means you've called endParameterChangeGesture without having previously called
// beginParameterChangeGesture. That might be fine in most hosts, but better to keep the
// calls matched correctly.
jassert (changingParams[parameterIndex]);
changingParams.clearBit (parameterIndex);
#endif
for (int i = listeners.size(); --i >= 0;)
if (auto* l = getListenerLocked (i))
l->audioProcessorParameterChangeGestureEnd (this, parameterIndex);
}
else
{
jassertfalse; // called with an out-of-range parameter index!
}
} }
} }
@@ -1340,7 +1368,8 @@ void AudioProcessorParameter::setValueNotifyingHost (float newValue)
// This method can't be used until the parameter has been attached to a processor! // This method can't be used until the parameter has been attached to a processor!
jassert (processor != nullptr && parameterIndex >= 0); jassert (processor != nullptr && parameterIndex >= 0);
return processor->setParameterNotifyingHost (parameterIndex, newValue);
setValue (newValue);
sendValueChangedMessageToListeners (newValue);
} }
void AudioProcessorParameter::beginChangeGesture() void AudioProcessorParameter::beginChangeGesture()
@@ -1348,7 +1377,25 @@ void AudioProcessorParameter::beginChangeGesture()
// This method can't be used until the parameter has been attached to a processor! // This method can't be used until the parameter has been attached to a processor!
jassert (processor != nullptr && parameterIndex >= 0); jassert (processor != nullptr && parameterIndex >= 0);
processor->beginParameterChangeGesture (parameterIndex);
#if JUCE_DEBUG && ! JUCE_DISABLE_AUDIOPROCESSOR_BEGIN_END_GESTURE_CHECKING
// This means you've called beginChangeGesture twice in succession without
// a matching call to endChangeGesture. That might be fine in most hosts,
// but it would be better to avoid doing it.
jassert (! isPerformingGesture);
isPerformingGesture = true;
#endif
ScopedLock lock (listenerLock);
for (int i = listeners.size(); --i >= 0;)
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());
} }
void AudioProcessorParameter::endChangeGesture() void AudioProcessorParameter::endChangeGesture()
@@ -1356,7 +1403,40 @@ void AudioProcessorParameter::endChangeGesture()
// This method can't be used until the parameter has been attached to a processor! // This method can't be used until the parameter has been attached to a processor!
jassert (processor != nullptr && parameterIndex >= 0); jassert (processor != nullptr && parameterIndex >= 0);
processor->endParameterChangeGesture (parameterIndex);
#if JUCE_DEBUG && ! JUCE_DISABLE_AUDIOPROCESSOR_BEGIN_END_GESTURE_CHECKING
// This means you've called endChangeGesture without having previously
// called beginChangeGesture. That might be fine in most hosts, but it
// would be better to keep the calls matched correctly.
jassert (isPerformingGesture);
isPerformingGesture = false;
#endif
ScopedLock lock (listenerLock);
for (int i = listeners.size(); --i >= 0;)
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());
}
void AudioProcessorParameter::sendValueChangedMessageToListeners (float newValue)
{
ScopedLock lock (listenerLock);
for (int i = listeners.size(); --i >= 0;)
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);
} }
bool AudioProcessorParameter::isOrientationInverted() const { return false; } bool AudioProcessorParameter::isOrientationInverted() const { return false; }
@@ -1372,6 +1452,18 @@ String AudioProcessorParameter::getText (float value, int /*maximumStringLength*
return String (value, 2); return String (value, 2);
} }
void AudioProcessorParameter::addListener (AudioProcessorParameter::Listener* newListener)
{
const ScopedLock sl (listenerLock);
listeners.addIfNotAlreadyThere (newListener);
}
void AudioProcessorParameter::removeListener (AudioProcessorParameter::Listener* listenerToRemove)
{
const ScopedLock sl (listenerLock);
listeners.removeFirstMatchingValue (listenerToRemove);
}
//============================================================================== //==============================================================================
bool AudioPlayHead::CurrentPositionInfo::operator== (const CurrentPositionInfo& other) const noexcept bool AudioPlayHead::CurrentPositionInfo::operator== (const CurrentPositionInfo& other) const noexcept
{ {


+ 2
- 2
modules/juce_audio_processors/processors/juce_AudioProcessor.h View File

@@ -1616,10 +1616,10 @@ private:
template <typename floatType> template <typename floatType>
void processBypassed (AudioBuffer<floatType>&, MidiBuffer&); void processBypassed (AudioBuffer<floatType>&, MidiBuffer&);
#if JucePlugin_Build_VST3
friend class AudioProcessorParameter;
friend class JuceVST3EditController; friend class JuceVST3EditController;
friend class JuceVST3Component; friend class JuceVST3Component;
#endif
Atomic<int> vst3IsPlaying { 0 }; Atomic<int> vst3IsPlaying { 0 };


+ 68
- 0
modules/juce_audio_processors/processors/juce_AudioProcessorParameter.h View File

@@ -199,10 +199,78 @@ public:
/** Returns the index of this parameter in its parent processor's parameter list. */ /** Returns the index of this parameter in its parent processor's parameter list. */
int getParameterIndex() const noexcept { return parameterIndex; } int getParameterIndex() const noexcept { return parameterIndex; }
//==============================================================================
/**
A base class for listeners that want to know about changes to an
AudioProcessorParameter.
Use AudioProcessorParameter::addListener() to register your listener with
an AudioProcessorParameter.
This Listener replaces most of the functionality in the
AudioProcessorListener class, which will be deprecated and removed.
*/
class JUCE_API Listener
{
public:
/** Destructor. */
virtual ~Listener() {}
/** Receives a callback when a parameter has been changed.
IMPORTANT NOTE: this will be called synchronously when a parameter changes, and
many audio processors will change their parameter during their audio callback.
This means that not only has your handler code got to be completely thread-safe,
but it's also got to be VERY fast, and avoid blocking. If you need to handle
this event on your message thread, use this callback to trigger an AsyncUpdater
or ChangeBroadcaster which you can respond to on the message thread.
*/
virtual void parameterValueChanged (int parameterIndex, float newValue) = 0;
/** Indicates that a parameter change gesture has started.
E.g. if the user is dragging a slider, this would be called with gestureIsStarting
being true when they first press the mouse button, and it will be called again with
gestureIsStarting being false when they release it.
IMPORTANT NOTE: this will be called synchronously, and many audio processors will
call it during their audio callback. This means that not only has your handler code
got to be completely thread-safe, but it's also got to be VERY fast, and avoid
blocking. If you need to handle this event on your message thread, use this callback
to trigger an AsyncUpdater or ChangeBroadcaster which you can respond to later on the
message thread.
*/
virtual void parameterGestureChanged (int parameterIndex, bool gestureIsStarting) = 0;
};
/** Registers a listener to receive events when the parameter's state changes.
If the listener is already registered, this will not register it again.
@see removeListener
*/
void addListener (Listener* newListener);
/** Removes a previously registered parameter listener
@see addListener
*/
void removeListener (Listener* listener);
//==============================================================================
/** @internal */
void sendValueChangedMessageToListeners (float newValue);
private: private:
//==============================================================================
friend class AudioProcessor; friend class AudioProcessor;
AudioProcessor* processor = nullptr; AudioProcessor* processor = nullptr;
int parameterIndex = -1; int parameterIndex = -1;
CriticalSection listenerLock;
Array<Listener*> listeners;
#if JUCE_DEBUG
bool isPerformingGesture = false;
#endif
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AudioProcessorParameter) JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AudioProcessorParameter)
}; };


Loading…
Cancel
Save