MagicFilterPlot assertion failure for low sampleRate [and proposed fix]

I ran into an assertion failure when I ran the EqualizerExample wearing my Air Pods Pro (24 kHz sampleRate), and this fixes it, but there is an offset with the XY slider that I’m looking into now:

--- a/modules/foleys_gui_magic/Visualisers/foleys_MagicFilterPlot.cpp
+++ b/modules/foleys_gui_magic/Visualisers/foleys_MagicFilterPlot.cpp
@@ -38,11 +38,14 @@
 namespace foleys
 {

+static const double NUM_FREQS { 300 };
+
 MagicFilterPlot::MagicFilterPlot()
 {
-    frequencies.resize (300);
+    frequencies.resize (NUM_FREQS);
+
     for (size_t i = 0; i < frequencies.size(); ++i)
-        frequencies [i] = 20.0 * std::pow (2.0, i / 30.0);
+      frequencies [i] = 0.5 * double(i) / frequencies.size(); // normalized Hz until sampleRate becomes available

     magnitudes.resize (frequencies.size());
 }
@@ -111,6 +114,15 @@ void MagicFilterPlot::createPlotPaths (juce::Path& path, juce::Path& filledPath,
 void MagicFilterPlot::prepareToPlay (double sampleRateToUse, int)
 {
     sampleRate = sampleRateToUse;
+
+    auto freqRatio = std::pow(2.0, (std::log2(0.9999 * sampleRate/2.0) - std::log2(20.0))/double(NUM_FREQS - 1));
+    double freq = 20.0;
+    for (size_t i = 0; i < frequencies.size(); ++i)
+    {
+      jassert(freq < sampleRate/2.0);
+      frequencies [i] = freq;
+      freq *= freqRatio;
+    }
 }

I see that the hardwired frequency parameter range exceeds sampleRate/2, which is clearly a problem.

When I hardwire the frequency parameter max to sampleRate/2, the XYDragComponent lines up with the Peak frequency at dc and sampleRate/2, but at the interior frequencies the peak is to the left of the XY slider. Could it be an incompatibility in foleys::Conversions::makeLogarithmicRange?

    auto freqParameter = std::make_unique<juce::AudioParameterFloat> (juce::ParameterID (prefix + IDs::paramFreq, 1),
                                                                      name + ": " + TRANS ("Frequency"),
                                                                      foleys::Conversions::makeLogarithmicRange<float>(20.0f, 12000.0f), /* half of LOWEST sampleRate */
                                                                      frequency,
                                                                      juce::String(),
                                                                      juce::AudioProcessorParameter::genericParameter,
                                                                      [](float value, int) { return (value < 1000) ?
                                                                        juce::String (value, 0) + " Hz" :
                                                                        juce::String (value / 1000.0) + " kHz"; },
                                                                      [](juce::String text) { return text.endsWith(" kHz") ?
                                                                        text.getFloatValue() * 1000.0f :
                                                                        text.getFloatValue(); });

Thank you for raising this. The EqualizerExample is indeed not optimised for variable sample rates. It was assumed it would be always in the same ballpark of 44.1 - 48 kHz. That is clearly an omission in the example.

I will have a look if it can be improved, especially where that assumption reaches into the module.