Help with pan-delay

Hi
I’m working on what I call a pan delay… the input signal is split into (say) 6 components, each of which then can have a separate delay time, pan in the stereo field, level and (if necessary) feedback.
I’ve only got as far as the delay part, but my code is not working as I expected. The level controls for the delay, only the last one is working. Any help would be much appreciated! (The code here only uses 3 components…)

void DelayAudioProcessor::prepareToPlay (double sampleRate, int samplesPerBlock)
{
    const double smoothTime = 1e-3;
    paramDelayTime.reset (sampleRate, smoothTime);
    paramFeedback.reset (sampleRate, smoothTime);
    paramMix.reset (sampleRate, smoothTime);
    
    paramDelayTime2.reset (sampleRate, smoothTime);
    paramFeedback2.reset (sampleRate, smoothTime);
    paramMix2.reset (sampleRate, smoothTime);
    
    paramDelayTime3.reset (sampleRate, smoothTime);
    paramFeedback3.reset (sampleRate, smoothTime);
    paramMix3.reset (sampleRate, smoothTime);

    //======================================

    float maxDelayTime = paramDelayTime.maxValue;
    delayBufferSamples = (int)(maxDelayTime * (float)sampleRate) + 1;
    if (delayBufferSamples < 1)
        delayBufferSamples = 1;

    delayBufferChannels = getTotalNumInputChannels();
    delayBuffer.setSize (delayBufferChannels, delayBufferSamples);
    delayBuffer.clear();

    delayWritePosition = 0;
    
    
    float maxDelayTime2 = paramDelayTime2.maxValue;
    delayBuffer2Samples = (int)(maxDelayTime2 * (float)sampleRate) + 1;
    if (delayBuffer2Samples < 1)
        delayBuffer2Samples = 1;

    delayBuffer2Channels = getTotalNumInputChannels();
    delayBuffer2.setSize (delayBuffer2Channels, delayBuffer2Samples);
    delayBuffer2.clear();

    delay2WritePosition = 0;
    
    
    float maxDelayTime3 = paramDelayTime3.maxValue;
    delayBuffer3Samples = (int)(maxDelayTime3 * (float)sampleRate) + 1;
    if (delayBuffer3Samples < 1)
        delayBuffer3Samples = 1;

    delayBuffer3Channels = getTotalNumInputChannels();
    delayBuffer3.setSize (delayBuffer3Channels, delayBuffer3Samples);
    delayBuffer3.clear();

    delay3WritePosition = 0;
}

void DelayAudioProcessor::releaseResources()
{
}

void DelayAudioProcessor::processBlock (AudioSampleBuffer& buffer, MidiBuffer& midiMessages)
{
    ScopedNoDenormals noDenormals;

    const int numInputChannels = getTotalNumInputChannels();
    const int numOutputChannels = getTotalNumOutputChannels();
    const int numSamples = buffer.getNumSamples();

    //======================================

    float currentDelayTime = paramDelayTime.getTargetValue() * (float)getSampleRate();
    float currentDelay2Time = paramDelayTime2.getTargetValue() * (float)getSampleRate();
    float currentDelay3Time = paramDelayTime3.getTargetValue() * (float)getSampleRate();
    float currentFeedback = paramFeedback.getNextValue();
    float currentFeedback2 = paramFeedback2.getNextValue();
    float currentFeedback3 = paramFeedback3.getNextValue();
    float currentMix = paramMix.getNextValue();
    float currentMix2 = paramMix2.getNextValue();
    float currentMix3 = paramMix3.getNextValue();

    int localWritePosition, localWritePosition2, localWritePosition3;

    for (int channel = 0; channel < numInputChannels; ++channel) {
        float* channelData = buffer.getWritePointer (channel);
        float* intermediateData = channelData;
        float* intermediateData2 = channelData;
        float* intermediateData3 = channelData;
        float* finalData = channelData;
        float* delayData = delayBuffer.getWritePointer (channel);
        float* delay2Data = delayBuffer2.getWritePointer (channel);
        float* delay3Data = delayBuffer3.getWritePointer (channel);
        
        localWritePosition = delayWritePosition;
        localWritePosition2 = delay2WritePosition;
        localWritePosition3 = delay3WritePosition;
        

        for (int sample = 0; sample < numSamples; ++sample) {
            const float in = channelData[sample];
            const float in2 = channelData[sample];
            const float in3 = channelData[sample];
            
            float out = 0.0f;
            float out2 = 0.0f;
            float out3 = 0.0f;

            float readPosition =
                fmodf ((float)localWritePosition - currentDelayTime + (float)delayBufferSamples, delayBufferSamples);
            float readPosition2 =
                fmodf ((float)localWritePosition2 - currentDelay2Time + (float)delayBuffer2Samples, delayBuffer2Samples);
            float readPosition3 =
                fmodf ((float)localWritePosition3 - currentDelay3Time + (float)delayBuffer3Samples, delayBuffer3Samples);
            
            int localReadPosition  = floorf (readPosition);
            int localReadPosition2 = floorf (readPosition2);
            int localReadPosition3 = floorf (readPosition3);

            if (localReadPosition != localWritePosition) {
                float fraction = readPosition - (float)localReadPosition;
                float delayed1 = delayData[(localReadPosition + 0)];
                float delayed2 = delayData[(localReadPosition + 1) % delayBufferSamples];
                out = out + delayed1 + fraction * (delayed2 - delayed1);

                intermediateData[sample] = in + currentMix * (out - in);
                delayData[localWritePosition] = in + out * currentFeedback;
            }

            if (++localWritePosition >= delayBufferSamples)
                localWritePosition -= delayBufferSamples;
            
            //----------------------------
            
            if (localReadPosition2 != localWritePosition2) {
                float fraction = readPosition2 - (float)localReadPosition2;
                float delayed1 = delay2Data[(localReadPosition2 + 0)];
                float delayed2 = delay2Data[(localReadPosition2 + 1) % delayBuffer2Samples];
                out2 = out2 + delayed1 + fraction * (delayed2 - delayed1);

                intermediateData2[sample] = in2 + currentMix2 * (out2 - in2);
                delay2Data[localWritePosition2] = in2 + out2 * currentFeedback2;
            }

            if (++localWritePosition2 >= delayBuffer2Samples)
                localWritePosition2 -= delayBuffer2Samples;
            
            //----------------------------
            
            if (localReadPosition3 != localWritePosition3) {
                float fraction = readPosition3 - (float)localReadPosition3;
                float delayed1 = delay3Data[(localReadPosition3 + 0)];
                float delayed2 = delay3Data[(localReadPosition3 + 1) % delayBuffer3Samples];
                out3 = out3 + delayed1 + fraction * (delayed2 - delayed1);

                intermediateData3[sample] = in3 + currentMix3 * (out3 - in3);
                delay3Data[localWritePosition3] = in3 + out3 * currentFeedback3;
            }
            
            if (++localWritePosition3 >= delayBuffer3Samples)
                localWritePosition3 -= delayBuffer3Samples;

            channelData[sample] = intermediateData[sample] + intermediateData2[sample] + intermediateData3[sample];
         //   finalData[sample] = intermediateData[sample] + intermediateData2[sample] + intermediateData3[sample];
          //  channelData[sample] = finalData[sample];
          /*
            // swap l/r
            if (channel == 0)
                delayLine.pushSample (1, input);
            else
                delayLine.pushSample (0, input * -1.0f); // invert polarity
           */
            
            
        }
    }

    delayWritePosition = localWritePosition;
    delay2WritePosition = localWritePosition2;
    delay3WritePosition = localWritePosition3;

    //======================================

   // for (int channel = numInputChannels; channel < numOutputChannels; ++channel)
   //     buffer.clear (channel, 0, numSamples);
}

Here is a screenshot of the controls:-

OK, some progress. The code below seems to work as expected… can anyone comment as to whether there are any serious mistakes in this?

void DelayAudioProcessor::processBlock (AudioSampleBuffer& buffer, MidiBuffer& midiMessages)
{
    ScopedNoDenormals noDenormals;
    

    const int numInputChannels = getTotalNumInputChannels();
    const int numOutputChannels = getTotalNumOutputChannels();
    buffer2 = buffer;
    buffer3 = buffer;
    const int numSamples = buffer.getNumSamples();
   

    //======================================

    float currentDelayTime = paramDelayTime.getTargetValue() * (float)getSampleRate();
    float currentDelay2Time = paramDelayTime2.getTargetValue() * (float)getSampleRate();
    float currentDelay3Time = paramDelayTime3.getTargetValue() * (float)getSampleRate();
    float currentFeedback = paramFeedback.getNextValue();
    float currentFeedback2 = paramFeedback2.getNextValue();
    float currentFeedback3 = paramFeedback3.getNextValue();
    float currentMix = paramMix.getNextValue();
    float currentMix2 = paramMix2.getNextValue();
    float currentMix3 = paramMix3.getNextValue();

    int localWritePosition, localWritePosition2, localWritePosition3;

    for (int channel = 0; channel < numInputChannels; ++channel) {
        float* channelData = buffer.getWritePointer (channel);
        float* channelData2 = buffer2.getWritePointer (channel);
        float* channelData3 = buffer3.getWritePointer (channel);
        float* intermediateData = channelData;
        float* intermediateData2 = channelData2;
        float* intermediateData3 = channelData3;
     //   float* finalData = channelData;
        float* delayData = delayBuffer.getWritePointer (channel);
        float* delay2Data = delayBuffer2.getWritePointer (channel);
        float* delay3Data = delayBuffer3.getWritePointer (channel);
        
        localWritePosition = delayWritePosition;
        localWritePosition2 = delay2WritePosition;
        localWritePosition3 = delay3WritePosition;
        

        for (int sample = 0; sample < numSamples; ++sample) {
            const float in = channelData[sample];
            const float in2 = channelData2[sample];
            const float in3 = channelData3[sample];
            
            float out = 0.0f;
            float out2 = 0.0f;
            float out3 = 0.0f;

            float readPosition =
                fmodf ((float)localWritePosition - currentDelayTime + (float)delayBufferSamples, delayBufferSamples);
            float readPosition2 =
                fmodf ((float)localWritePosition2 - currentDelay2Time + (float)delayBuffer2Samples, delayBuffer2Samples);
            float readPosition3 =
                fmodf ((float)localWritePosition3 - currentDelay3Time + (float)delayBuffer3Samples, delayBuffer3Samples);
            
            int localReadPosition  = floorf (readPosition);
            int localReadPosition2 = floorf (readPosition2);
            int localReadPosition3 = floorf (readPosition3);

            if (localReadPosition != localWritePosition) {
                float fraction = readPosition - (float)localReadPosition;
                float delayed1 = delayData[(localReadPosition + 0)];
                float delayed2 = delayData[(localReadPosition + 1) % delayBufferSamples];
                out = out + delayed1 + fraction * (delayed2 - delayed1);
                
                

                intermediateData[sample] = in + currentMix * (out - in);
                delayData[localWritePosition] = in + out * currentFeedback;
            }

            if (++localWritePosition >= delayBufferSamples)
                localWritePosition -= delayBufferSamples;
            
            //----------------------------
            
            if (localReadPosition2 != localWritePosition2) {
                float fraction = readPosition2 - (float)localReadPosition2;
                float delayed1 = delay2Data[(localReadPosition2 + 0)];
                float delayed2 = delay2Data[(localReadPosition2 + 1) % delayBuffer2Samples];
                out2 = out2 + delayed1 + fraction * (delayed2 - delayed1);

                intermediateData2[sample] = in2 + currentMix2 * (out2 - in2);
                delay2Data[localWritePosition2] = in2 + out2 * currentFeedback2;
            }

            if (++localWritePosition2 >= delayBuffer2Samples)
                localWritePosition2 -= delayBuffer2Samples;
            
            //----------------------------
            
            if (localReadPosition3 != localWritePosition3) {
                float fraction = readPosition3 - (float)localReadPosition3;
                float delayed1 = delay3Data[(localReadPosition3 + 0)];
                float delayed2 = delay3Data[(localReadPosition3 + 1) % delayBuffer3Samples];
                out3 = out3 + delayed1 + fraction * (delayed2 - delayed1);

                intermediateData3[sample] = in3 + currentMix3 * (out3 - in3);
                delay3Data[localWritePosition3] = in3 + out3 * currentFeedback3;
            }
            
            if (++localWritePosition3 >= delayBuffer3Samples)
                localWritePosition3 -= delayBuffer3Samples;

            channelData[sample] = intermediateData[sample] + intermediateData2[sample] + intermediateData3[sample];
        
            
            
        }
    }

    delayWritePosition = localWritePosition;
    delay2WritePosition = localWritePosition2;
    delay3WritePosition = localWritePosition3;

    //======================================

   // for (int channel = numInputChannels; channel < numOutputChannels; ++channel)
   //     buffer.clear (channel, 0, numSamples);
}

I can assume that your delay will introduce serious audio glitches if you change the Time parameter in real time. You don’t need to roll your own delay, have a look at DSPModulePluginDemo for a ready to use delay implementation by the Juce team.