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?
tempWBufferPos
andtempDBufferPos
between calls, you should be good. $\endgroup$