Hi!
I am an intermediate developer, new to JUCE. I have been working on a custom implementation of a circularBuffer that I really like, and a super short (flangy effect) delay processing block that goes with it, but it creates the weirdest artifacts. I know there are tutorials out there and I have looked in this forum, but everybody has a different implementation, and I would really like to make mine work (plus I think it might be valuable for someone else to read, I think it’s a cool implementation). I am going to post here the crucial pieces of code in case someone can help me out.
The effect is part of a larger system, and has only one parameter handled elsewhere: intensity. The larger the intensity the shorter the delay (very very short) and the greater the feedback on the delay line.
Main processing block:
void Flanger::processLocal (juce::AudioBuffer<float>& buffer, juce::MidiBuffer& midibuffer)
{
int numSamples = buffer.getNumSamples();
int delayInSamps = static_cast<int> (((1.0f-intensity_) * SAMP_DELAY_MULTIPLIER + SAMP_DELAY_OFFSET) * mSampleRate);
delayLine->setReadWriteDelay(delayInSamps);
delayLine->write(buffer);
delayLine->readTo(auxiliarBuffer, numSamples, false);
// Feedback the delayed signal
float feedbackGain = std::pow(intensity_,EXP_FACTOR) * MAX_FEEDBACK;
delayLine->feed(auxiliarBuffer,
delayLine->writeHead-numSamples,
0,
-1,
feedbackGain);
// Read with the feedback
delayLine->readTo(auxiliarBuffer, numSamples);
// Perform drywet
float wetAmount =0.0f;
if(intensity_ < 0.5){
wetAmount = std::pow(0.7f * 2 * intensity_, EXP_FACTOR);
} else {
wetAmount = std::pow(0.7f, EXP_FACTOR);
}
juce::dsp::AudioBlock<float> auxiliarBlock(auxiliarBuffer);
juce::dsp::AudioBlock<float> dryBlock(buffer);
auxiliarBlock.multiplyBy(wetAmount);
dryBlock.multiplyBy(1.0f-wetAmount); // not dry anymore
dryBlock.add(auxiliarBlock);
dryBlock.multiplyBy(1.0f-intensity_*GAIN_INTENSITY_ADJUSTMENT);
}
Some key circular buffer functions:
int CircularBuffer::write(juce::AudioBuffer<float>& input, int writeHead, bool advanceWriteHead){
int numSamples = input.getNumSamples();
int numChannels = input.getNumChannels();
jassert(numChannels == buffer.getNumChannels());
for (int channel = 0; channel < numChannels; ++channel){
const float* inputData = input.getReadPointer(channel);
float* toStorage = buffer.getWritePointer(channel);
for (int sample = 0; sample < numSamples; ++sample){
toStorage[adjustIndex(writeHead + sample)] = inputData[sample];
}
}
if(advanceWriteHead)
return advanceHead(writeHead, numSamples);
else return writeHead;
}
void CircularBuffer::write(juce::AudioBuffer<float>& input, bool advanceWriteHead){
writeHead = write(input, writeHead, advanceWriteHead);
}
void CircularBuffer::advanceWriteHead(int howMuch){
writeHead = advanceHead(writeHead, howMuch);
}
void CircularBuffer::advanceReadHead(int howMuch){
readHead = advanceHead(readHead, howMuch);
}
int CircularBuffer::adjustIndex(int index){
return ((index % buffer.getNumSamples()) // Adjust the bounds
+buffer.getNumSamples()) // Make it positive in case it was negative
% buffer.getNumSamples(); // Adjust bounds again in case it was already positive
}
When intensity is low (0.27), you can start to see artifacts like this one, original signal is up, processed on the bottom:
When intensity grows even more artifacts happen:
The artifacts length matches the buffer size I am testing with, but they don’t start with every new buffer.
Thanks in advance!
[EDIT]: forgot to mention that if I delete the “feed” line in the main processing block, there are no artifacts, so it seems like the problem might be in how I am performing feedback, but I keep debugging and reading the code and I can’t find the issue