FR: beginChangeGesture/endChangeGesture improvement

AudioProcessorParameter::beginChangeGesture is using isPerformingGesture flag to detect (in debug build) if beginChangeGesture has already been called and asserts if it has happened. This works fine in most cases.

I suggest to replace that flag with a counter that is incremented in beginChangeGesture and decremented in endChangeGesture. begin/end messages would only be sent when the counter is one.

This would allow multiple components (or MIDI routing) to control the same parameter at the same time without asserts (and extranous host-plugin messages). This can easily occur ie. when a parameter is adjusted at the same time from eg. a slider and external MIDI.

Example scenario:

Slider mouse down -> beginChangeGesture
MIDI input -> beginChangeGesture -> setValueNotifyingHost -> endChangeGesture
Slider mouse up -> endChangeGesture

With the current JUCE implementation we get an assert when we receive MIDI assigned to the parameter on the second line.

  • Jussi
    diff --git a/modules/juce_audio_processors/processors/juce_AudioProcessor.cpp b/modules/juce_audio_processors/processors/juce_AudioProcessor.cpp
index e6aeae5a4..5bba7fe92 100644
--- a/modules/juce_audio_processors/processors/juce_AudioProcessor.cpp
+++ b/modules/juce_audio_processors/processors/juce_AudioProcessor.cpp
@@ -1452,11 +1452,9 @@ AudioProcessorParameter::AudioProcessorParameter() noexcept {}
 
 AudioProcessorParameter::~AudioProcessorParameter()
 {
-   #if JUCE_DEBUG && ! JUCE_DISABLE_AUDIOPROCESSOR_BEGIN_END_GESTURE_CHECKING
     // This will fail if you've called beginChangeGesture() without having made
     // a corresponding call to endChangeGesture...
-    jassert (! isPerformingGesture);
-   #endif
+    jassert (gestureCount.get() == 0);
 }
 
 void AudioProcessorParameter::setValueNotifyingHost (float newValue)
@@ -1469,14 +1467,13 @@ void AudioProcessorParameter::beginChangeGesture()
 {
     // This method can't be used until the parameter has been attached to a processor!
     jassert (processor != nullptr && parameterIndex >= 0);
+    
+    jassert(gestureCount.get() >= 0);
 
-   #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
+    ++gestureCount;
+   
+    if (gestureCount.get() > 1)
+        return;
 
     ScopedLock lock (listenerLock);
 
@@ -1499,13 +1496,12 @@ void AudioProcessorParameter::endChangeGesture()
     // This method can't be used until the parameter has been attached to a processor!
     jassert (processor != nullptr && parameterIndex >= 0);
 
-   #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
+    jassert (gestureCount.get() > 0);
+
+    --gestureCount;
+
+    if (gestureCount.get() > 0)
+        return;
 
     ScopedLock lock (listenerLock);
 
diff --git a/modules/juce_audio_processors/processors/juce_AudioProcessorParameter.h b/modules/juce_audio_processors/processors/juce_AudioProcessorParameter.h
index f661dcb19..c373da7c1 100644
--- a/modules/juce_audio_processors/processors/juce_AudioProcessorParameter.h
+++ b/modules/juce_audio_processors/processors/juce_AudioProcessorParameter.h
@@ -292,10 +292,7 @@ private:
     CriticalSection listenerLock;
     Array<Listener*> listeners;
     mutable StringArray valueStrings;
-
-   #if JUCE_DEBUG
-    bool isPerformingGesture = false;
-   #endif
+    juce::Atomic<int> gestureCount { 0 };
 
     JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AudioProcessorParameter)
 };
1 Like

What’s with all those CTRL+M at the end of each line?

Thanks! Fixed now.