Lambda Issue

I am having an issue with adding a variable to a lambda wave shaping function. I am not sure what im doing wrong. I want to use a knob to change the slope. Am I not approaching this properly?

Thanks!

Here is the code text:

struct MultibandDistortion
{
    
    MultibandDistortion()
    {
    
    }
    
    void prepare (const juce::dsp::ProcessSpec& spec)
    {
        
        waveShaper1.functionToUse = [this] (float x)
        {
            float originalX = x;
            
            x = (std::tanh(x * 2.f));
            
            x = (x-(slope*x)) / (slope - (2.f * slope * std::abs(x)) + 1.f);
            
            return x;
        };
    }
    
    void reset() noexcept
    {
        
    }
    
    template <typename ProcessContext>
    void process (const ProcessContext& context) noexcept
    {
        waveShaper1.process (context);
    }
    
    
    
    
    dsp::WaveShaper<float> waveShaper1;
    
    float slope = 1.f;
    
};

I never used the DSP module, but after a quick look ; i guess that it can’t compile as a lambda can only be converted to a function pointer if it does not capture.

You could try also with a custom Function parameter (instead of default) for WaveShapper.

template<typename FloatType, typename Function = FloatType (*) (FloatType)>

The documentation here is not helpful either:

https://docs.juce.com/master/structdsp_1_1WaveShaper.html#ab24e80dd88150137f5814f689c900bb6

However, the source code for the class shows the usage of Function as this:

typename Function = FloatType (*) (FloatType)

    Function functionToUse;
    SampleType JUCE_VECTOR_CALLTYPE processSample (SampleType inputSample) const noexcept
    {
        return functionToUse (inputSample);
    }

You can customize this template parameter with a different type that follows the same pattern:

juce::dsp::WaveShaper<float, std::function<float(float)>> waveShaper1;

Once you do this, You’ll be able to capture this:

However, you should capture this in a juce::WeakReference, just in case the lifetime of that callable ends up being different from the lifetime of your waveshaper.
capturing this in a juce::WeakReference<> or juce::Component::SafePointer<> is just a good habit to get into.

Once you make this change to the template parameters, it’ll compile. See this godBolt as proof:

1 Like

Notice that in that case i suppose that thread safety must be properly handled.

in that case meaning?

Thanks so much Matkat!
Works like a charm!

Wouldn’t have gotten here without you (everyone should take his course, worth every penny)

Thanks Nicolas too!

3 Likes

Capturing this.

AFAIK the member values are reachable frrom the DSP perform and from any GUI handling at the same time. I suppose it is what the OP needs to manage saying “I want to use a knob to change the slope”.

You need to use an AudioParameterFloat to represent the slope.
retrieve the value via the atomic get() or getValue(). i can’t remember which one at the moment.

Give your class an AudioParameterFloat* member variable.
Initialize it when the class is constructed from your APVTS.

MultibandDistortion(APVTS& apvts)
{
    slope = dynamic_cast<juce::AudioParameterFloat*>(apvts.getParameter("Slope"));
    jassert(slope != nullptr);
}

you can capture the slope in the lambda like this:

waveShaper1.function = [slope = this->slope](float x)
{
    // 'slope' is a pointer, captured by copy.
    return x;
};
1 Like

this has come up several times in the forums, might be worth updating the docs to reflect this?

1 Like