0
$\begingroup$

I am learning to write audio plugins using VST3 sdk. I have an issue implementing 2 delays in serie, the audio starts crackling.

Here is the processing code:

tresult PLUGIN_API StereoProcessor::process (Vst::ProcessData& data)
{
    //--- First : Read inputs parameter changes-----------
    // (You can ignore this)
    if (data.inputParameterChanges)
    {
        int32 numParamsChanged = data.inputParameterChanges->getParameterCount();
        for (int32 index = 0; index < numParamsChanged; index++)
        {
            IParamValueQueue* paramQueue = data.inputParameterChanges->getParameterData(index);
            if (paramQueue)
            {
                ParamValue value;
                int32 sampleOffset;
                int32 numPoints = paramQueue->getPointCount();
                switch (paramQueue->getParameterId())
                {
                case kLevelId:
                    if (paramQueue->getPoint(numPoints - 1, sampleOffset, value) == kResultTrue)
                        mLevel = value;
                    break;
                case kDelayId:
                    if (paramQueue->getPoint(numPoints - 1, sampleOffset, value) == kResultTrue)
                        mDelay = value;
                    break;
                case kBypassId:
                    if (paramQueue->getPoint(numPoints - 1, sampleOffset, value) == kResultTrue)
                    {
                        mBypass = (value > 0.5f);
                    }
                    break;
                }
            }
        }
    }

    if (data.numSamples > 0)
    {
        SpeakerArrangement arr;
        getBusArrangement(kOutput, 0, arr);
        int32 numChannels = SpeakerArr::getChannelCount(arr);

        // do something in Bypass or mono: copy input to output
        // (You can ignore this)
        if (mBypass || numChannels == 1)
        {
            for (int32 channel = 0; channel < numChannels; channel++)
            {
                float* inputChannel = data.inputs[0].channelBuffers32[channel];
                float* outputChannel = data.outputs[0].channelBuffers32[channel];

                for (int32 sample = 0; sample < data.numSamples; sample++)
                    outputChannel[sample] = inputChannel[sample];
            }
            return kResultTrue;
        }

        // PROCESS STARTS HERE
        // 1st delay size, w is for Width as this one is short and is meant to provide "width"
        int32 wDelayInSamples = std::max<int32>(processSetup.sampleRate / 2'000, (int32)(mWidth * mWidthBufferSize));
        // 2dn delay size, d is for delay as it will be longer (although can be short too)
        int32 dDelayInSamples = (int32)(mDelay * mDelayBufferSize);
        // swith to activate/deactivate the second delay (if it's value is 0)
        float delaySwitch = wDelayInSamples ? 1 : 0;
        for (int32 channel = 0; channel < numChannels; channel++)
        {
            float* inputChannel = data.inputs[0].channelBuffers32[channel];
            float* outputChannel = data.outputs[0].channelBuffers32[channel];
            // the first part of mBuffer is dedicated to the width delay
            float* wBuffer = mBuffer[channel];
            // and the second to the 2nd delay
            float* dBuffer = wBuffer + mWidthBufferSize;

            int32 tempWBufferPos = mWidthBufferPos;
            int32 tempDBufferPos = mDelayBufferPos;

            for (int32 sample = 0; sample < data.numSamples; sample++)
            {
                float tempSample = *inputChannel++;
                float tempWSample = wBuffer[tempWBufferPos];
                // apply 1st delay
                *outputChannel++ = tempSample + mLevel*tempWSample;
                // apply 2nd delay
                wBuffer[tempWBufferPos] = tempSample + delaySwitch*dBuffer[tempDBufferPos];
                dBuffer[tempDBufferPos] = tempWSample;
                
                // adjust buffer positions (circular buffer)
                tempWBufferPos++;
                if (tempWBufferPos >= wDelayInSamples)
                    tempWBufferPos = 0;
                
                tempDBufferPos++;
                if (tempDBufferPos >= dDelayInSamples)
                    tempDBufferPos = 0;             
            }
        }
        // adjust buffer positions (circular buffer)
        mWidthBufferPos += data.numSamples;
        while (wDelayInSamples && mWidthBufferPos >= wDelayInSamples)
            mWidthBufferPos -= wDelayInSamples;

        mDelayBufferPos += data.numSamples;
        while (dDelayInSamples && mDelayBufferPos >= dDelayInSamples)
            mDelayBufferPos -= dDelayInSamples;
        }
    return kResultTrue;
}

The buffer and delay sizes are initialized here:

//------------------------------------------------------------------------
tresult PLUGIN_API StereoProcessor::setActive (TBool state)
{
    //--- called when the Plug-in is enable/disable (On/Off) -----
    SpeakerArrangement arr;
    if (getBusArrangement(kOutput, 0, arr) != kResultTrue)
        return kResultFalse;
    int32 numChannels = SpeakerArr::getChannelCount(arr);
    if (numChannels == 0)
        return kResultFalse;

    if (state)
    {
        mWidthBufferSize = processSetup.sampleRate / 10 + 0.5;
        mDelayBufferSize = processSetup.sampleRate + 0.5;
        int32 size = mWidthBufferSize + mDelayBufferSize;
        mBuffer = (float**)std::malloc(numChannels * sizeof(float*));
        for (int32 channel = 0; channel < numChannels; channel++)
        {
            mBuffer[channel] = (float*)std::calloc(size, sizeof(float));    // 1 second delay max
        }
        mWidthBufferPos = 0;
        mDelayBufferPos = 0;
    }
    else
    {
        if (mBuffer)
        {
            for (int32 channel = 0; channel < numChannels; channel++)
            {
                std::free(mBuffer[channel]);
            }
            std::free(mBuffer);
            mBuffer = nullptr;
        }
    }
    return AudioEffect::setActive(state);
}

If I remove the last loop at the end (and set the second delay switch to 0) then I don't get any issue.

        //float delaySwitch = wDelayInSamples ? 1 : 0;
        float delaySwitch = 0;
        
        // ...
        
        /*
        mDelayBufferPos += data.numSamples;
        while (dDelayInSamples && mDelayBufferPos >= dDelayInSamples)
            mDelayBufferPos -= dDelayInSamples;
        }
        */

Is there anything wrong with my code? So how can I run two delay in series without audio issues?

$\endgroup$
8
  • 1
    $\begingroup$ You have two lines of comment in this massive code dump. You will get higher chance of an answer if you explain how your code is working and reference the key parts in the code with proper comments. It would also help if you would provide the unit test code that verifies that the code is doing what you think it's doing. If you don't have a unit test, I would start with that. $\endgroup$
    – Hilmar
    Commented Aug 21, 2021 at 14:31
  • $\begingroup$ Ok, I've added some comments. As I mentionned, when removing the second while loop the audio doesn't crackle and the first delay works perfectly, I find it a bit hard to provide unit testing here. $\endgroup$
    – Abitbol
    Commented Aug 21, 2021 at 15:05
  • $\begingroup$ Unit testing is pretty straight forward: Build a test rig that allows you to run test vectors through the algorithm and then you can use "known" test signals: pulse trains, sine waves, etc and compare what you get with what you expect.. How else do you test your code ? $\endgroup$
    – Hilmar
    Commented Aug 21, 2021 at 15:08
  • $\begingroup$ So I don't understand why you need these while loops in the first place. You implement the circular addressing already in the main loop. If you simply save tempWBufferPos and tempDBufferPos between calls, you should be good. $\endgroup$
    – Hilmar
    Commented Aug 21, 2021 at 15:24
  • $\begingroup$ I see, it's a first for me at programming dsp so I am not used to it. Also I am using windows+visual studio which is a first as well so I am not really productive. Thanks, it runs a lot better now removing the redundant while loops! I am not getting the effect I wanted though, there seems to be a feedback loop in the signal. $\endgroup$
    – Abitbol
    Commented Aug 21, 2021 at 16:49

0

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Browse other questions tagged or ask your own question.