Struct,universal function call & clamp

Hi,

I am currently writing a patch which is more complex than my usual sine/triangle/square oscillators, and I ran into a compilation error that I don’t understand.

I defined a Svf struct representing a state variable filter. This struct has methods to set frequence and resonance and to filter one sample or a buffer of samples. I use the universal function call everywhere, but for one method it fails to compile with error: Cannot implicitly convert ‘Svf’ to ‘Svf&’

What am I doing wrong there? I compile using soul errors.

processor Dummy
{

    output stream float audioOut;

    /* State variable filter */
    struct Svf
    {
        /* Internal factors of the filter */
        float g, r, h;

        /* Current states of the filter */
        float state1, state2;
    }

    void initialize(Svf& svf)
    {
        svf.setFreqRes(0.01f, 100.0f);
        svf.state1 = 0.0f;
        svf.state2 = 0.0f;
    }

    void setFreqRes(Svf& svf, float frequency, float resonance)
    {
        svf.g = float(tan(pi * frequency));
        svf.r = 1.0f / resonance;
        svf.h = 1.0f / (1.0f + svf.r * svf.g + svf.g * svf.g);
    }

    float[3] filter(Svf& svf, float in)
    {
        float hp, bp, lp;
        float[3] result;

        hp         = (in - svf.r * svf.state1 - svf.g * svf.state1 - svf.state2) * svf.h;
        bp         = svf.g * hp + svf.state1;
        svf.state1 = svf.g * hp + bp;
        lp         = svf.g * bp + state2;
        svf.state2 = svf.g * bp + lp;

        result[0] = lp;
        result[1] = bp;
        result[2] = hp;

        return result;
    }

    void filter(Svf& svf, float[] in, float[] out, clamp<2> type)
    {
        static_assert(in.size == out.size);

        for (int index; index < in.size; index++)
        {
            // Apply filter to each element of in and write the requested output (lp, bf or hp) to out
            out[index] = svf.filter(in[index])[type];
        }
    }

    void run()
    {
        Svf svf;
        float[24] inputBuffer;
        float[24] outputBuffer;

        inputBuffer[:] = 0.0f;
        outputBuffer[:] = 0.0f;

        // These calls compile
        svf.initialize();
        svf.setFreqRes(0.5f, 1.5f);

        // This call fails to compile with message: error: Cannot implicitly convert 'Svf' to 'Svf&'
        svf.filter(inputBuffer, outputBuffer, 0);

        loop
        {
            audioOut << 0.0f;
            advance();
        }
    }

}

OK the message from the compiler was misleading. There were other errors in the code which caused the filter method not to compile, but SOUL complained about the reference instead of those errors. This version compiles:

processor Dummy(int bufferSize)
{

    output stream float audioOut;

    /* State variable filter */
    struct Svf
    {

        /* Internal factors of the filter */
        float g, r, h;

        /* Current states of the filter */
        float state1, state2;

    }

    void initialize(Svf& svf)
    {
        svf.setFreqRes(0.01f, 100.0f);
        svf.state1 = 0.0f;
        svf.state2 = 0.0f;
    }

    void setFreqRes(Svf& svf, float frequency, float resonance)
    {
        svf.g = float(tan(pi * frequency));
        svf.r = 1.0f / resonance;
        svf.h = 1.0f / (1.0f + svf.r * svf.g + svf.g * svf.g);
    }

    float[3] filter(Svf& svf, float in)
    {
        float hp, bp, lp;
        float[3] result;

        hp         = (in - svf.r * svf.state1 - svf.g * svf.state1 - svf.state2) * svf.h;
        bp         = svf.g * hp + svf.state1;
        svf.state1 = svf.g * hp + bp;
        lp         = svf.g * bp + svf.state2;
        svf.state2 = svf.g * bp + lp;

        result[0] = lp;
        result[1] = bp;
        result[2] = hp;

        return result;
    }

    void filter(Svf& svf, float[bufferSize] in, float[bufferSize] out, clamp<2> type)
    {
        for (int index; index < in.size; index++)
        {
            // Apply filter to each element of in and write the requested output (lp, bf or hp) to out
            out[index] = svf.filter(in[index])[type];
        }
    }

    void run()
    {
        Svf svf;
        float[24] inputBuffer;
        float[24] outputBuffer;

        inputBuffer[:] = 0.0f;
        outputBuffer[:] = 0.0f;

        svf.initialize();
        svf.setFreqRes(0.5f, 1.5f);
        svf.filter(inputBuffer, outputBuffer, 0);

        loop
        {
            audioOut << 0.0f;
            advance();
        }
    }

}

Uh no the bug was not fixed. I declared a graph using the Dummy processor, and soul errors outputs the same error message as before. Replacing clamp<2> type by int type fixed it.

Is clamp type forbidden as a function parameter?

processor Dummy(int bufferSize)
{

    output stream float audioOut;

    /* State variable filter */
    struct Svf
    {
        /* Internal factors of the filter */
        float g, r, h;

        /* Current states of the filter */
        float state1, state2;
    }

    void initialize(Svf& svf)
    {
        svf.setFreqRes(0.01f, 100.0f);
        svf.state1 = 0.0f;
        svf.state2 = 0.0f;
    }

    void setFreqRes(Svf& svf, float frequency, float resonance)
    {
        svf.g = float(tan(pi * frequency));
        svf.r = 1.0f / resonance;
        svf.h = 1.0f / (1.0f + svf.r * svf.g + svf.g * svf.g);
    }

    float[3] filter(Svf& svf, float in)
    {
        float hp, bp, lp;
        float[3] result;

        hp         = (in - svf.r * svf.state1 - svf.g * svf.state1 - svf.state2) * svf.h;
        bp         = svf.g * hp + svf.state1;
        svf.state1 = svf.g * hp + bp;
        lp         = svf.g * bp + svf.state2;
        svf.state2 = svf.g * bp + lp;

        result[0] = lp;
        result[1] = bp;
        result[2] = hp;

        return result;
    }

    void filter(Svf& svf, float[bufferSize] in, float[bufferSize] out, int type)
    {
        for (int index; index < in.size; index++)
        {
            // Apply filter to each element of in and write the requested output (lp, bf or hp) to out
            out[index] = svf.filter(in[index])[type];
        }
    }

    void run()
    {
        Svf svf;
        float[24] inputBuffer;
        float[24] outputBuffer;

        inputBuffer[:] = 0.0f;
        outputBuffer[:] = 0.0f;

        // These calls compile
        svf.initialize();
        svf.setFreqRes(0.5f, 1.5f);
        svf.filter(inputBuffer, outputBuffer, 0);

        loop
        {
            audioOut << 0.0f;
            advance();
        }
    }

}

graph SvfTest [[main]]
{

    output stream float audioOut;

    let
    {
        dummy = Dummy(24);
    }

    connection
    {
        dummy.audioOut -> audioOut;
    }

}

Ah, right - thanks for reporting, that is definitely the wrong error message! I’ll add a unit test for this. I think it should probably have managed to compile, but I’ll dig into it to make sure, and at least try to make sure the error is more sensible!

I think I found another issue with the compiler, but I need to clean the code to get a smaller test case. Do you prefer me to report it on the github tracker or on this forum?

Don’t worry, I’ve got it down to a failing one-line test :slight_smile:

It’s the clamp parameter that’s confusing it for some reason. I think it’ll work if you change that to an int, or do a cast, e.g. svf.filter(inputBuffer, outputBuffer, clamp<2> (0)); but obviously that’s silly, the compiler should manage to figure that out for itself. Should be an easy fix, thanks!

Thanks Jules, the cast works!

Another error case for you :wink: This one fails with the error: Internal compiler error: "“object != nullptr” failed at soul::LinkedList::Iterator::removeNext:244"

processor Dummy(int bufferSize)
{
    output stream float audioOut;

    void run()
    {
        float[bufferSize] in;
        //in[0:1] = 0.0f;  // This one works
        in[0:2] = 0.0f;  // This one fails
     
        loop
        {
            audioOut << 0.0f;
            advance();
        }
    }
}

graph SvfTest [[main]]
{
    output stream float audioOut;

    let
    {
        dummy = Dummy(24);
    }

    connection
    {
        dummy.audioOut -> audioOut;
    }
}

Blimey, you’re really managing to dig up some edge-cases for us! Thanks, will get this sorted too!