Audio plugin - unexpected send overloaded signal out to DAW(Ableton Live 10)

Hello

I’ve been struggling with this problem, about my audio plugin unexpected send overloaded signal out to DAW(Ableton Live 10). Mostly on right track channel, only registred ones on left channel. It can happen when using it, or even if no audio is feed into it.

Its a delay plugin, and I’m using processorChain( adding JUCE Ladderfilter, My Own chorus/fanger fx).

It would be too much asking you to read my code, but if anyone has had a similary problem I would like to hear from you. Could it be an audio buffer not handled correct? ProcessorChain calls prepare(), proces() and reset() - and I don’t do anything in my reset() in chorus fx - could that be a problem?

Any ideas about where to look is much appreciated - thanks.

meter

This often happens from uninitialized audio data making it’s way to your buffer output.

be sure you’re clearing unused channels, as well as setting new float arrays and audio buffers to zero before using them.

This is a huge culprit in circular buffers of delay lines. They need to be zero’d before you begin writing data to them.

Post it up!

1 Like

Thanks for your reply.

I do this in the prepare() function. I post it here below so you can see. It is my Chorus/flanger template class that I’m using within a processorChain juce::dsp::ProcessorChain<juce::dsp::LadderFilter, ChorusFlanger> processorChain; adding it to my feedbacked delayed signal. I think the problem might be in this class, because the problem only happens when the Chorus effect is not bypassed. The plugin can work perfectly fine using the chorus effect, but then suddenly plugin burst out audible overloaded audio on meter track(DAW).

#pragma once

#include "../JuceLibraryCode/JuceHeader.h"

#define MAX_DELAY_TIME 2

//=========================================================================================================


template <typename T>
class ChorusFlanger
{

public:
    //=============================================================================

    ChorusFlanger(){
        mCurrentSampleRate = 0.0;
        
        /* we set nullptr because we don't no the sample rate yet, and are not ready to instantiate audio data/ how big buffer is*/
        mCircularBufferLeft = nullptr;
        mCircularBufferRight = nullptr;
        
        
        mCircularBufferWriteHead = 0;
        mCircularBufferLength = 0;
        
        
        mFeedBackLeft = 0;
        mFeedBackRight = 0;
        
        mLFOPhase = 0;
        
        mDryWetSmoothing = 0;
        mDepthSmoothing = 0;
        mPhaseSmoothing = 0;
    
    
    
}



//==============================================================================
    void prepare (const juce::dsp::ProcessSpec& spec)
    {
        mCurrentSampleRate = spec.sampleRate;
        
       
        
        mLFOPhase = 0;
        
        
        /* our bufferLength - time in samples we need to store - only (int) delay time allowed*/
        mCircularBufferLength = spec.sampleRate * MAX_DELAY_TIME;
        
        
        
        /* Here we check for nullptr, and create and set the size of our floating point array - the correct length of our buffers*/
        if (mCircularBufferLeft == nullptr )
        {
            //mCircularBufferLeft.reset( new float[static_cast<int>(mCircularBufferLength)]);
            mCircularBufferLeft = std::make_unique<float[]>(mCircularBufferLength);
            
        }
        //mCircularBufferLeft.reset();

        zeromem(mCircularBufferLeft.get(), mCircularBufferLength * sizeof(float));
        
        if (mCircularBufferRight == nullptr )
        {
            //mCircularBufferRight.reset(new float[static_cast<int>( mCircularBufferLength)]);
            mCircularBufferRight = std::make_unique<float[]>(mCircularBufferLength);
            
        }
        //mCircularBufferRight.reset();

        zeromem(mCircularBufferRight.get(), mCircularBufferLength * sizeof(float));
        
        
        mCircularBufferWriteHead = 0;
        
        
    }
    
    template <typename ProcessContext>
    void process (const ProcessContext& context) noexcept
    {
        const auto& inputBlock = context.getInputBlock();
        auto& outputBlock = context.getOutputBlock();
       

        
        const auto numInSamples = inputBlock.getNumSamples();
        
        
        
        ScopedNoDenormals noDenormals;
     
        
        
        
        
        
        
        
        
        const float* leftChannel =  inputBlock.getChannelPointer(0);  //context.getWritePointer(0);
        const float* RightChannel = inputBlock.getChannelPointer(1); //context.getWritePointer(1);
        
        
        
        /*Iterate through all samples in audio buffer, and proces buffer data in loop*/
        for (int i = 0; i < numInSamples; i++)
        {
            
            /*we store data in circular buffer + adding the feedback data back, se below. Se diagram in notes */
            mCircularBufferLeft.get()[mCircularBufferWriteHead] = leftChannel[i] + mFeedBackLeft;
            mCircularBufferRight.get()[mCircularBufferWriteHead] = RightChannel[i] + mFeedBackRight;
            
           
            
            /* Generating the left LFO output*/
            float lfoOutLeft = sin(2*M_PI * mLFOPhase);
            
            
            
            /* Moving our LFO phase forward*/
            mLFOPhase += mRateParameter / mCurrentSampleRate;
            
            /* to keep mLFOPhase between 0-1 (is exactly one cycle)  */
            if(mLFOPhase >= 1)
            {
                mLFOPhase -= 1;
            }
            
            mPhaseSmoothing = mPhaseSmoothing - 0.0001*(mPhaseSmoothing - mPhaseOffsetParameter);
            
            /* calculate the right LFO phase*/
            float lfoPhaseRight = mLFOPhase + mPhaseSmoothing;                     //*mPhaseOffsetParameter;
            
            
            if(lfoPhaseRight > 1)
            {
                lfoPhaseRight -= 1;
            }
            
            /* Generating the right LFO output*/
            float lfoOutRight = sin(2*M_PI * lfoPhaseRight);
            
            
            /*We apply smooting, and add it to lfoOut*/
            mDepthSmoothing = mDepthSmoothing - 0.0001*(mDepthSmoothing - mDepthParameter);
            
            /* we set the range of the LFO, control the depth of lfo + apply smoothing*/
            lfoOutLeft *= mDepthSmoothing;
            lfoOutRight *= mDepthSmoothing;
            
            
            /*we map lfo output to our desired delay times(chorus/flanger)*/
            
            float lfoOutMappedLeft = 0;
            float lfoOutMappedRight = 0;
            
            
            /* Chorus */
            if (mTypeParameter == 1)
            {
                
                lfoOutMappedLeft = jmap<float>(lfoOutLeft, -1.0f, 1.0f, 0.005f, 0.03f);
                
                lfoOutMappedRight = jmap<float>(lfoOutRight, -1.0f, 1.0f, 0.005f, 0.03f);
                
                
            }
            
            if (mTypeParameter == 2)
            {
                /* Flanger */
                lfoOutMappedLeft = jmap<float>(lfoOutLeft, -1.0f, 1.0f, 0.001f, 0.005f);
                lfoOutMappedRight = jmap<float>( lfoOutRight, -1.0f, 1.0f, 0.001f, 0.005f);
                
            }
            
            
            
            
            
            /* Calculate delay lenght in samples. Equal to delaylength = samplerate * delayTimesInSeconds. */
            float delayTimeSamplesLeft = mCurrentSampleRate * lfoOutMappedLeft;
            
            
            
            float delayTimeSamplesRight = mCurrentSampleRate * lfoOutMappedRight;
            
            
            
            /* calculate the left read head position, we want to read from */
            float delayReadHeadLeft = mCircularBufferWriteHead - delayTimeSamplesLeft;
            
            if (delayReadHeadLeft < 0)
            {
                delayReadHeadLeft += mCircularBufferLength;
            }
            
            
            /* calculate the right read head position, we want to read from */
            float delayReadHeadRight = mCircularBufferWriteHead - delayTimeSamplesRight;
            
            if (delayReadHeadRight < 0)
            {
                delayReadHeadRight += mCircularBufferLength;
            }
            
            
            
            /* here we calculate our linear interpolation values for left channel (to put in lin_interp() */
            int readHeadLeft_x = static_cast<int>(delayReadHeadLeft); // Int is needed for mCircularBuffer array index access
            int readHeadLeft_x1 = readHeadLeft_x + 1;
            /* the float remainder value, used for interval between x and x1, where we want to compute a interpolated value */
            float readHeadFloatLeft = delayReadHeadLeft - readHeadLeft_x;
            
            /*If we exceed our buffer*/
            if(readHeadLeft_x1 >= mCircularBufferLength)
            {
                readHeadLeft_x1 -= mCircularBufferLength;
            }
            
            /* here we calculate our linear interpolation values for right channel (to put in lin_interp() */
            int readHeadRight_x = static_cast<int>(delayReadHeadRight); // Int is needed for mCircularBuffer array index access
            int readHeadRight_x1 = readHeadRight_x + 1;
            /* the float remainder value, used for interval between x and x1, where we want to compute a interpolated value */
            float readHeadFloatRight = delayReadHeadRight - readHeadRight_x;
            
            /*If we exceed our buffer*/
            if(readHeadRight_x1 >= mCircularBufferLength)
            {
                readHeadRight_x1 -= mCircularBufferLength;
            }
            
            
            /* We generate our interpolated delayed output audio data we want to sum with original audio buffer. added to feedback(left/right), then into circularbuffer above, and summed below with original audio buffer buffer.addSample()*/
            float delay_sample_left = lin_interp(mCircularBufferLeft.get()[readHeadLeft_x], mCircularBufferLeft.get()[readHeadLeft_x1], readHeadFloatLeft);
            
            float delay_sample_right = lin_interp(mCircularBufferRight.get()[readHeadRight_x], mCircularBufferRight.get()[readHeadRight_x1], readHeadFloatRight);
            
            /* here we scale our feedback audio data (* mFeedbackParameter) avoiding overloaded volume in DAW */
            mFeedBackLeft = delay_sample_left * mFeedbackParameter;
            mFeedBackRight = delay_sample_right * mFeedbackParameter;
            
            
            
            
            /*we increment writeHead*/
            mCircularBufferWriteHead++;
            
            
            /* if end of array, we set writeHead to begining - circularbuffer concept */
            if(mCircularBufferWriteHead >= mCircularBufferLength)
            {
                
                mCircularBufferWriteHead = 0;
                
            }
            
            /*We apply smoothing, and add it below (buffer.setSample) replacing *mDryWetParameter*/
            mDryWetSmoothing = mDryWetSmoothing - 0.0001*(mDryWetSmoothing - mDryWetParameter);
            
            
            
            
            /* here we add delayed signal/data to the original buffer data. */
    
            outputBlock.setSample(0, i, inputBlock.getSample(0, i) * (1 - mDryWetSmoothing) +
                              delay_sample_left * mDryWetSmoothing );
            outputBlock.setSample(1, i, inputBlock.getSample(1, i) * (1 - mDryWetSmoothing) +
                              delay_sample_right * mDryWetSmoothing );
            
        }
        
    }
    
    void reset() noexcept
    {
        
    }
    
    float lin_interp(float inSampleX, float inSampleY, float inFloatPhase)
    {
     
        
        return (1 - inFloatPhase) * inSampleX + inFloatPhase * inSampleY;
        
        
        
    }
    
    float getMDryWetParameter() const {
        return mDryWetParameter;
    }
    
    void setMDryWetParameter(T mDryWet) {
        mDryWetParameter = mDryWet;
    }
    
    float getMDepthParameter() const {
        return mDepthParameter;
    }
    
    void setMDepthParameter(T mDepth) {
        mDepthParameter = mDepth;
    }
    
    float getMRateParameter() const {
        return mRateParameter;
    }
    
    void setMRateParameter(T mRate) {
        mRateParameter = mRate;
    }
    
    float getMPhaseOffsetParameter() const {
        return mPhaseOffsetParameter;
    }
    
    void setMPhaseOffsetParameter(T mPhaseOffset) {
        mPhaseOffsetParameter = mPhaseOffset;
    }
    
    float getMFeedbackParameter() const {
        return mFeedbackParameter;
    }
    
    void setMFeedbackParameter(T mFeedback) {
        mFeedbackParameter = mFeedback;
    }
    
    float getMTypeParameter() const {
        return mTypeParameter;
    }
    
    void setMTypeParameter(T mType) {
        mTypeParameter = mType;
    }
    
    
    
    
private:
    //===============================================================================
    
    double mCurrentSampleRate;
    
    /*this is where we store our audio data from original audio buffer*/
    std::unique_ptr<float[]> mCircularBufferLeft;
    std::unique_ptr<float[]> mCircularBufferRight;
    
    
    /*keep track of current postion in array/buffer we write to*/
    int mCircularBufferWriteHead;
    
    /*the size of our array/buffer*/
    int mCircularBufferLength;
    
    
    /* mFeedBack(L/R) Is the output of our delayed audio data, that we store/feed back in our circularBuffer + the original audio buffer data*/
    float mFeedBackLeft;
    float mFeedBackRight;
    
    
    /* LFO data */
    float mLFOPhase;
    
    /* Parameter declaration:  we are using the depth,rate, phase to modulate the delayReadHead, instead of manually in delay*/
    float mDryWetParameter;
    float mDepthParameter;
    float mRateParameter;
    float mPhaseOffsetParameter;
    float mFeedbackParameter;
    float mTypeParameter;
    
    /*Smoothing variables*/
    float mDryWetSmoothing;
    float mDepthSmoothing;
    float mPhaseSmoothing;
    
    
    
    
    JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ChorusFlanger)
    
    
    
    
    
    
};

Hehe, looks suspiciously like my Kadenze course code!

A few notes:

if (mCircularBufferRight == nullptr )

^ looks like you’re using unique_ptrs, so you don’t need these braces, just reset them to the right length when prepareToPlay is called.

Other than that nothing is immediately sticking out as the obvious culprit.

Does it happened regularly enough for you to reproduce it?

Sometimes wrapping a check for nan or inf at the end of the process block, which triggers a breakpoint if true at the end the process block can be really helpful for stopping program execution when an issue like this occurs.

Is there a change in samplerate?

It could also be that because of those checks for nullptr, when sample rates change the buffers are not resizing to the correct length, and then you have a buffer overrun because your mCircularBufferLength is larger than the buffer itself.

I’d say try removing those braces first off.

Yes, hehe, part of the code is from a intro JUCE course I took earlier this year at kadenze, but I have reimplemented it as a template class so that it can be used in a processorChain. That way I can also reuse it in other projects.

Jake_Penn - are you Jacob Penn(Output) course teacher at kadenze. If so, thank you again for a great course - I’ve learned a lot.

Yes, but it does not happen every time I run it. Sometime it can run for 30 minutes before an error occur.

Not that I know of. I’ve place a std::out << “prepare called”; in the prepare() functions. That way I will know if sample rate change unexpected.

I will try remove those braces, so I only do this:

mCircularBufferLeft = std::make_unique<float>(mCircularBufferLength);

zeromem(mCircularBufferLeft.get(), mCircularBufferLength * sizeof(float));

I’l also try wrapping a check for nan or inf at the end of the process block. Dont know how, but I google around and see how it is done. Assume you mean this http://www.cplusplus.com/reference/cmath/nan-function/.

Thanks for the tip, I appreciate you taken the time.

Guilty as charged.

These are other places in the JUCE source:

    jassert (! std::isnan (value));
    jassert (! std::isinf (value));

Try generating the output sample and passing it through these before into the output buffer. At the very least until you track down the issue, this will stop the execution so you can go through and find where things are becoming funny

Thanks. I’l try that.

As I wrote, I have placed std::cout’s in prepare() checking when prepare() is called. I’ve found out that prepare initially is called 5 times - what! how strange is that.

I’ve removed those braces. Running the plugin for more 30 min (playing a loop) does not produce error…hmmmm

Sample rate has not changed running the plugin.
I tried manually changing the sample rate, and that does not produce error.

Hello again.
Yesterday I was optimistic, but today the problem is back. Sample rate does not change running plugin. Referring to the posted code above, I placed jassert in for loop (processing buffer) before outputBlock.

jassert (! std::isnan (delay_sample_left));
jassert (! std::isinf (delay_sample_left));

jassert (! std::isnan (delay_sample_right));
jassert (! std::isinf (delay_sample_right));

outputBlock.setSample(0, i, inputBlock.getSample(0, i) * (1 - mDryWetSmoothing) + delay_sample_left * mDryWetSmoothing );

outputBlock.setSample(1, i, inputBlock.getSample(1, i) * (1 - mDryWetSmoothing) + delay_sample_right * mDryWetSmoothing );

}

And it produce a breakpoint (see image).

Looking at the debug window and in code, it looks like readHeadRight_x1 exceeds mCircularBufferlength ( (Sample rate)44.100 * 2(max delay time) = 88.200) by 1. And readHeadRight_x1 is set to 1 (look in code, and debug win)

And readHeadFloatRight is 0 (look in code, and debug win). Therefore delay_sample_right is assigned NaN because readHeadFloatRight(0) is put into the interpolation function.

Maybe you have an idea about how to deal with it.

Read your code in reverse, somewhere you have to have assigned a nan to it (most likely as a result of a divide by zero, so focus on divisions)

I don’t know your lin_interp, but I’d assume if readHeadRight_x and readHeadRight_x1 are identical, a division by zero is likely with the naive implementation.

If you look in my posted code above you can find lin_interp:

float lin_interp(float inSampleX, float inSampleY, float inFloatPhase)
{
return (1 - inFloatPhase) * inSampleX + inFloatPhase * inSampleY;
}

inFloatPhase param comes from ReadHeadFloatRight that is 0 at breakpoint time.

readHeadRight_x and readHeadRight_x1 are not identical.

int readHeadRight_x = static_cast(delayReadHeadRight);
int readHeadRight_x1 = readHeadRight_x + 1;

I’l keep looking

        /*If we exceed our buffer*/
        if(readHeadRight_x1 >= mCircularBufferLength)
        {
            readHeadRight_x1 -= mCircularBufferLength;
        }

^^ this should be stopping the buffer overrun seen in your screenshot.

I’d check what mCircularBufferLength is and see why this is not wrapping the read head around your buffer.

I can’t see the value of mCircularBufferLength in your screenshot, but if it’s indeed 88200, then this should have wrapped it around to 0

Good news is, now that you’ve got the problem area you should be able to easily figure out what’s going on and fix it : )

It is 88.200. But readHeadRight_x1 is 88.201 before it is wrapped - so, it’s not set to 0, but 1, as screen shot shows. Sorry if I’m naive, but why not just do this:

if(readHeadRight_x1 >= mCircularBufferLength)
{
readHeadRight_x1 = 0;
}

But you are right. Now I have the problem area and know were to look. The problem could also be the lin_interp() - thats were NaN comes from.

Thanks for your time :smiley: -)

Because if your pointer was moved by more than one sample, you would expect to end up at a different start position.

Thank you both.

Hey I’m retaking the course as I wasn’t fully focused before (bought the second one over Black Friday sale) – for some reason I’m having code issues that it seems like others aren’t having.

jmap is throwing an error – I see the two template types for jmap - one with 3 params, one with 5. For some reason the 3 param one is throwing a ‘2. Candidate function template not viable: requires 3 arguments, but 5 were provided’.

Hey @grimmwerks,

You might get better responses on the course forum, but be sure to post your code with your questions or it’s very difficult.

This error with jmap is common when types aren’t specified

jmap(.5, 1, 10)

won’t work

while:

jmap(.5, 1.f, 10.f) 

will.

Good luck,

J

won’t as well, .5 will be a double, while the 1.f is a float :wink:

These templated functions are picky, they won’t accept any implicit cast, you will have to explicitly cast the arguments to be the same, since the template preprocessor refuses to choose from:

template<typename Type>
Type jmap (Type, Type, Type);
// could be:
float jmap (float, float, float)
// or
double jmap (double, double, double)
// but not
double jmap (double, float, float)

If it wasn’t a template, a float would be implicitly casted to a double and all good…

1 Like

Hello @grimmwerks. I asked a similar question on kadenze course forum. If you search for “jmap” you will find it. Bruce responded like this:

Hey Michael, things like this usually depend on the leniency of your compiler – some compilers are more strict than others. When using a jmap, which is a “template” function, the template needs to know that all arguments are of the same templated type, which in this case is a float. Good job solving this issue!

 jmap((float)lfoOut, -1.f, 1.f, 0.005f, 0.03f);

------end Bruce

In my code I do this:

jmap<float>(lfoOutLeft, -1.0f, 1.0f, 0.005f, 0.03f);

Since this is a template function, I think this is better. Hope this help.

Speaking of templates functions/classes. @daniel and @Jake_Penn, I have solved my problem. I could’t find any code that could produce a nan/Inf. Instead the issue was that I did’t use the typename in my template class

template <typename Type>
class ChorusFlanger    

...............some code using Type

So, I simply replaced float with Type in my code ( class is declared as float: juce::dsp::ProcessorChain<juce::dsp::LadderFilter<float>, ChorusFlanger<float>> processorChain )

It has been 3-4 weeks now, and no error - all good. But I don’t understand why this could cause the problem.