Hey there,
I’m trying to implement a plugin using the rt-wdf library by Maximilian Rest to create a analog-sounding distortion (and because I’m a litte intrigued by the method). The processing of samples with the high-level functions provided by the library is pretty straight-foreward and works for the example Fender-Tonestack-circuit (in real time) on my machine, but - as one would expect - introduces aliasing. I’m therefore using the Oversampling-class from the juce::dsp module to oversample the audioBuffer before processing the samples with the wave-digital-filter (also previously described and implemented in C++/JUCE by @maxprod).
Unfortunately, with the included oversampling, the plugin seems to lose it’s real time capability. Even with 2 or 4 times oversampling, the plugin causes the audio-playback in Reaper to stutter, having to stop and process every half a second or so. Also the CPU-load skyrockets to about 75%.
Here is the code from the prepareToPlay and process methods, OVERSAMPLING_FACTOR being a macro defined in the header-file and oversampler defined as:
juce::dsp::Oversampling<float>* oversampler = new juce::dsp::Oversampling<float> (getTotalNumInputChannels(), OVERSAMPLING_FACTOR, juce::dsp::Oversampling<float>::FilterType::filterHalfBandFIREquiripple, true, false);
also in the header-file.
void RtwdfPluginAudioProcessor::prepareToPlay (double sampleRate, int samplesPerBlock)
{
juce::dsp::ProcessSpec spec;
spec.sampleRate = sampleRate;
spec.maximumBlockSize = samplesPerBlock;
spec.numChannels = getTotalNumInputChannels();
oversampler->reset ();
oversampler->initProcessing(spec.maximumBlockSize);
thisWdfTree = new wdfTonestackTree();
thisWdfTree->initTree();
thisWdfTree->setSamplerate(OVERSAMPLING_FACTOR * this->getSampleRate());
thisWdfTree->adaptTree();
}
void RtwdfPluginAudioProcessor::processBlock (juce::AudioBuffer<float>& buffer, juce::MidiBuffer& midiMessages)
{
juce::ScopedNoDenormals noDenormals;
auto totalNumInputChannels = getTotalNumInputChannels();
auto totalNumOutputChannels = getTotalNumOutputChannels();
float bass = apvts.getParameter("BASS")->getValue();
float mid = apvts.getParameter("MID")->getValue();
float treble = apvts.getParameter("TREBLE")->getValue();
thisWdfTree->setParam(0, bass);
thisWdfTree->setParam(1, mid);
thisWdfTree->setParam(2, treble);
for (auto i = totalNumInputChannels; i < totalNumOutputChannels; ++i)
buffer.clear (i, 0, buffer.getNumSamples());
auto audioBlock = juce::dsp::AudioBlock<float>(buffer);
auto context = juce::dsp::ProcessContextReplacing<float>(audioBlock);
//oversampling:
auto oversamplingAudioBlock = oversampler->processSamplesUp(context.getInputBlock());
for (int channel = 0; channel < totalNumInputChannels; ++channel)
{
auto* channelPtr = oversamplingAudioBlock.getChannelPointer(channel);
for (int sample = 0; sample < oversamplingAudioBlock.getNumSamples(); sample++)
{
thisWdfTree->setInputValue(*(channelPtr+sample)); //access AudioBlock-data via pointer
thisWdfTree->cycleWave();
*(channelPtr+sample) = thisWdfTree->getOutputValue(); //same access as two lines above but in reverse
}
}
oversampler->processSamplesDown(context.getOutputBlock());
}
I’m not sure, what I could do to speed up the whole thing, or even if there isn’t anything wrong with the process that causes the audio to stutter. The switch between audioBlock and single-sample processing could also be introducing errors, maybe? I’m not yet ready to accept that the WDF-method itself is to computationally expensive, since it’s been implemented before, and was said to be real-time compatible.
If anyone could help me by suggesting speed-up-methods or pointing out any mistakes in the code in this regard, I would appreciate it.
Thanks in advance people!




