Basic DSP Question wrt DSPModulePlugInDemo.h

#1

I want to modify DSPModulePlugInDemo.h to include an arbitrary transfer function. Like y=f(x) where x is the input sample and y is the resulting output sample.

There’s a bit in the code where std::tanh is passed into Waveshaper. The compiler won’t let me substitute anything that requires additional parameters (eg. std::pow(a,b)). I’m not much experienced in C++.

Is there a way to define an arbitrary transfer function and pass it into Waveshaper? Or else perform it on the block samples directly (hopefully using the context already created in this file)?

Thank you.

JUCE 5.4.3
MacOS 10.13.6 (High Sierra)
Xcode 10.1

0 Likes

#2

You can replace std::tanh with any function that takes a float and returns a float.

You can define a function outside of the DspModulePluginDemoAudioProcessor class, for instance:

float multiplyByTheAnswer(float x)
{
    return x * 42.0f;
}

and then replace std::tanh by that function:

           waveShapers    { { multiplyByTheAnswer }, { dsp::FastMathApproximations::tanh } },

You can also use a lambda, to define the function inline:

           waveShapers    { { [](float x) { return x * 42.0f; } }, { dsp::FastMathApproximations::tanh } },

I hope this helps.

0 Likes

#3

The outside function works. The lambda inline evokes “No viable conversion from ‘void’ to ‘float (*)(float)’”. Thanks again.

0 Likes

#4

I should add that your lambda inline function does indeed work. My error occurs when substituting { (float x) { return std::pow(a,x); } }. Maybe it has something to do with the math function?

0 Likes

#5

Where is a defined ? If you defined it outside of the lambda, you need to capture it:

  • either by reference
[&a](float x) { return std::pow(a, x) ; }
  • or by value:
[a](float x) { return std::pow(a, x) ; }

If a is constant (for instance a=2), you can simply replace a by its value:

[] (float x) { return std::pow(2, x) ; }
0 Likes

#6

The WaveShaper doesn’t allow lambdas with captures because the WaveShaper internally stores the function only as a function pointer. (A lambda without captures can be converted into a function pointer.)

1 Like

#7

@Xenakios of course! Thanks for the correction.

0 Likes

#8

The Juce WaveShaper doesn’t work with functions with extra parameters nor with lambdas that have captures.

0 Likes

#9

Instead of calling waveshaper, how do I process the audio samples directly (either block-by-block or sample-by-sample)? For example, the demo file (DSPModulePluginDemo.h) creates “waveshaperContext”. Can I use that context for performing an arbitrary DSP transfer function? E.g. my own little inline y=f(x) equation where x are the input samples/block and y are the transformed replacement output samples/block? Or is that context not suitable? Either way, an example of this that fits into that file’s framework would be terrific.

Thanks for the responses. They’ve been really helpful.

0 Likes

#10

You can directly manipulate the samples of the AudioBuffer you are given. You are not forced to use anything in the Juce DSP module.

For example, to invert the polarity of the samples in the AudioBuffer, you can do something like :

void MyAudioPlugin::processBlock(AudioBuffer<float>& buffer, MidiBuffer& midibuffer)
{
  for (int i=0;i<buffer.getNumSamples();++i)
  {
     for (int j=0;j<buffer.getNumChannels();++j)
     {
       buffer.setSample(j,i,-buffer.getSample(j,i));
     }
  }
}
0 Likes

#11

Success, thank you so much Xenakios! Does this seem about right? In order to re-use the existing “context”, do I need to create these new AudioBlock instances?

For the time being, I’m modifying the DSPModulePluginDemo.h because of the other features it provides while I’m brand new to JUCE. I wanted to try and use the existing waveshaperContext because it has already taken into account things like mono/stereo and oversampling.

// Original
if (isPositiveAndBelow (waveshaperIndex, numWaveShapers) )
{
    // New
    if (waveshaperIndex == 1) // Temporarily highjack this to run my substitute code
    {
        dsp::AudioBlock<float> myInputBlock = waveshaperContext.getInputBlock();  // Is there an alternative to this?
        dsp::AudioBlock<float> myOutputBlock = waveshaperContext.getOutputBlock();  // Is there an alternative to this?
        for (int i=0;i<myInputBlock.getNumSamples();++i)
        {
            for (int j=0;j<myInputBlock.getNumChannels();++j)
            {
                myOutputBlock.setSample(j,i, 0.25f * myInputBlock.getSample(j,i));  // Just to see if it works
            }
        }
    }
    else   // Original
    {
        waveShapers[waveshaperIndex].process (waveshaperContext);
        if (waveshaperIndex == 1)
            clipping.process (waveshaperContext);
        waveshaperContext.getOutputBlock() *= 0.7f;
    }
}
0 Likes

#12

You can use 3 back-ticks characters to start and end a code block.

```
int main()
{
    return 42;
} 
```
0 Likes