Hey!
I am trying to build a simple chorus plugin using DSP modules such as the Fractional Delay Line and the Oscillator class (for creating the LFO). I believe I have been able to set up the delay correctly, but I get audible artifacts that seem to come from modulating the delay line (The artifacts sound periodic and change when I adjust the LFO Frequency/Amplitude parameters).
I have attached the preparetoplay method and processblock method below, but if you would like me to post also my attribute delcarations in my header file, I am happy to show that too!
void ChorusAudioProcessor::prepareToPlay (double sampleRate, int samplesPerBlock)
{
mSampleRate = sampleRate;
//prepare lfo
juce::dsp::ProcessSpec lfoSpec = { sampleRate,samplesPerBlock,getMainBusNumOutputChannels() };
lfo.prepare(lfoSpec);
//type of oscillator
lfo.initialise([](float x){return std::sin(x);}, 128);
//prepare delay line
mDelayLine = juce::dsp::DelayLine<float, juce::dsp::DelayLineInterpolationTypes::Lagrange3rd>(sampleRate * MAXDELAY);
juce::dsp::ProcessSpec delayLineSpec = { sampleRate,samplesPerBlock,getMainBusNumOutputChannels() };
mDelayLine.prepare(delayLineSpec);
//set size of delaybuffer
mDelayBuffer.setSize(2, sampleRate * MAXDELAY);
}
And this is my Process Block code:
void ChorusAudioProcessor::processBlock (juce::AudioBuffer<float>& buffer, juce::MidiBuffer& midiMessages)
{
juce::ScopedNoDenormals noDenormals;
auto totalNumInputChannels = getTotalNumInputChannels();
auto totalNumOutputChannels = getTotalNumOutputChannels();
for (auto i = totalNumInputChannels; i < totalNumOutputChannels; ++i)
buffer.clear (i, 0, buffer.getNumSamples());
////////////////////// MY CODE //////////////////////////
//retrieve parameters from gui
auto centerDelayInMillis = mParamTree.getRawParameterValue("DELAYID")->load();
auto wet = mParamTree.getRawParameterValue("DRYWETID")->load();
auto depth = mParamTree.getRawParameterValue("LFODEPTHID")->load(); //in milliseconds
auto lforate = mParamTree.getRawParameterValue("LFORATEID")->load();
//derived quantities
auto centerDelayInSamples = centerDelayInMillis * 0.001 * mSampleRate;
float depthInSamples = depth * 0.001 * mSampleRate;
//update lfo and retrieve the phase offset for the delay
lfo.setFrequency(lforate);
for (int channel = 0; channel < totalNumInputChannels; ++channel) {
auto* BufferWritePtr = buffer.getWritePointer(channel);
for (int sample = 0; sample < buffer.getNumSamples(); ++sample)
{
//push dry signal into delay buffer
auto drySignal = BufferWritePtr[sample];
mDelayLine.pushSample(channel, drySignal);
//update lfo and retrieve signal from delay buffer, the max function is to ensure our delay is never negative
float instantaneousOffsetInSamples = std::max(depthInSamples * lfo.processSample(0.0f), 0.0f);
float instanteousDelayedSignal = mDelayLine.popSample(channel, centerDelayInSamples + instantaneousOffsetInSamples);
//combine wet and dry signal
BufferWritePtr[sample] = (wet * instanteousDelayedSignal) + (1.0f - wet)*drySignal;
}
}
}