DelayLine - how to pop them samples

I’m trying to implement the DelayLine class in the DSP module, I would like to modulate the delay time in order to play with some various dsp effects. Would someone mind taking a look at this and showing me the error of my ways!

Here’s how I constructed it in my processor.h

juce::dsp::DelayLine<float, juce::dsp::DelayLineInterpolationTypes::Linear> mDelayLine;

This is my prepare to play function!

juce::dsp::ProcessSpec spec;
spec.sampleRate = sampleRate;
spec.maximumBlockSize = samplesPerBlock;
spec.numChannels = getTotalNumOutputChannels();

mDelayLine.reset();
mDelayLine.prepare(spec);

This is my process block!

void FeedPitchAudioProcessor::processBlock (juce::AudioBuffer<float>& buffer, juce::MidiBuffer& midiMessages)

{

juce::ScopedNoDenormals noDenormals;
auto totalNumInputChannels  = getTotalNumInputChannels();
auto totalNumOutputChannels = getTotalNumOutputChannels();

// if you've got more output channels than input clears extra outputs
for (auto i = totalNumInputChannels; i < totalNumOutputChannels; ++i)
    buffer.clear (i, 0, buffer.getNumSamples());

const int bufferLength = buffer.getNumSamples();

mSliderDelayTime = treeState.getRawParameterValue(DELAY_TIME_ID)->load();

for (int channel = 0; channel < totalNumInputChannels; ++channel)
{
    auto* inData = buffer.getReadPointer(channel);
    auto* outData = buffer.getWritePointer(channel);

    for (int i = 0; i < bufferLength; i++)
    {
        mDelayLine.pushSample(channel, inData[i]);
        outData[i] = mDelayLine.popSample(channel, mSliderDelayTime);
    }
} 

This is my first post on the forum so apologies if there are some rookie errors. Much appreciated! Henry.

I see a couple of problems :

  • You don’t seem to be initializing the DelayLine with a maximum size. The default constructor gives it a size of 4 samples, you will probably want construct it with at least 44100 samples or something… Like juce::dsp::DelayLine<float, juce::dsp::DelayLineInterpolationTypes::Linear> mDelayLine{44100};
  • You are not smoothing the delay time from the parameter, which will lead to audio glitches when changing the delay time. You could use a SmoothedValue or a simple 1st order low pass filter for that.
1 Like

Just to check - mSliderDelayTime - is this measured in ‘number of samples’? If it is in seconds or milliseconds, you will need to convert it to number of samples by multiplying it by the sample rate (assuming the variable is measured in seconds).

1 Like

Yes, mSliderDelayTime is measured in number of samples, ranging from 1 - 500 in 1 sample steps.

Thanks for the reply!

Ah okay yes makes sense, I’ve now initialized with a default sample rate.

Smoothing the delay was on my list of things to do! But wanted to get some sort of delayed effect first as cannot even get that right now. I can hear the audio glitches if I move the slider quickly now at least! So I think that’s a step forward. But still no delay effect.
SampleType popSample (int channel, SampleType delayInSamples = -1, bool updateReadPointer = true);
Does delayInSamples need to be a negative number? Tried it and doesn’t make a difference.
Many thanks for your help!

Disclamer: I roll my own delay lines so don’t use the inbuilt JUCE ones.

By hearing the “delay effect” I assume you mean the gradual decaying echo effect? It looks like you are just replacing the signal in your audio buffer with the output of the delay line. This will be delaying the signal but because you are only hearing a delayed output it will sound exactly the same as the input signal - just with a bit of latency. To get a delay with a couple of edits…

for (int channel = 0; channel < totalNumInputChannels; ++channel)
{
    auto* data = buffer.getWritePointer(channel);

    for (int i = 0; i < bufferLength; i++)
    {
        mDelayLine.pushSample(channel, data[i]);
        data[i] += mDelayLine.popSample(channel, mSliderDelayTime);
    }
}

I have changed it so you are using only one pointer - only one is needed as they are pointing to the same thing (write pointers can read as well). This is of course unless pushSample requires a read pointer. Also have changed the = in your loop to a += so that the delayed signal isn’t replacing the input signal; it will be combined. This means you will hear both the original and delayed signal. From here you could introduce a feedback to get the classic decaying echo effect.

Hope this helps!

1 Like

Excellent the += has done the job so I can hear the mix of the two signals now, makes total sense that before I was only hearing the delayed signal, so thank you! Makes sense about the write pointer so I have only one write pointer now like suggested.

And by “delay effect” I mean to automate the delay time using a phasor as per this Max MSP tutorial to achieve pitch shifting!
https://www.youtube.com/watch?v=uyzY_ZP54pA (there’s three parts as it’s old YouTube 10min constraints I think)

Thanks for the help!

1 Like

Great to hear it’s working!

Very cool, I hadn’t thought about doing pitch shifting that way before, thanks for linking that.

1 Like

Yes same glad it’s working!

Awesome, if you try it and get it working let me know! Would love to hear it in action.