How can I Offset an Audio and Add It to Itself?

Hi, I am trying to offset an audio by a certain delay time and then add it to itself? I do not want to do a delay line with a circular buffer. I want to have a long buffer to store the offset audio. It is like you copy the track on the first track of a DAW and then put the copied audio on the second track but slightly delayed from the audio on the first track. My goal is to have a continuous echo of the first audio. Here is the code I modified:

/*
  ==============================================================================

    This file contains the basic framework code for a JUCE plugin processor.

  ==============================================================================
*/

#include "PluginProcessor.h"
#include "PluginEditor.h"

//==============================================================================
CircularBufferDelayAudioProcessor::CircularBufferDelayAudioProcessor()
#ifndef JucePlugin_PreferredChannelConfigurations
    : AudioProcessor(BusesProperties()
#if !JucePlugin_IsMidiEffect
#if !JucePlugin_IsSynth
                         .withInput("Input", juce::AudioChannelSet::stereo(), true)
#endif
                         .withOutput("Output", juce::AudioChannelSet::stereo(), true)
#endif
                         ),
      params(*this, nullptr, "Parameters", createParameters())
#endif
{
}

CircularBufferDelayAudioProcessor::~CircularBufferDelayAudioProcessor() {
}

//==============================================================================
const juce::String CircularBufferDelayAudioProcessor::getName() const {
    return JucePlugin_Name;
}

bool CircularBufferDelayAudioProcessor::acceptsMidi() const {
#if JucePlugin_WantsMidiInput
    return true;
#else
    return false;
#endif
}

bool CircularBufferDelayAudioProcessor::producesMidi() const {
#if JucePlugin_ProducesMidiOutput
    return true;
#else
    return false;
#endif
}

bool CircularBufferDelayAudioProcessor::isMidiEffect() const {
#if JucePlugin_IsMidiEffect
    return true;
#else
    return false;
#endif
}

double CircularBufferDelayAudioProcessor::getTailLengthSeconds() const {
    return 0.0;
}

int CircularBufferDelayAudioProcessor::getNumPrograms() {
    return 1; // NB: some hosts don't cope very well if you tell them there are 0 programs,
              // so this should be at least 1, even if you're not really implementing programs.
}

int CircularBufferDelayAudioProcessor::getCurrentProgram() {
    return 0;
}

void CircularBufferDelayAudioProcessor::setCurrentProgram(int index) {
}

const juce::String CircularBufferDelayAudioProcessor::getProgramName(int index) {
    return {};
}

void CircularBufferDelayAudioProcessor::changeProgramName(int index, const juce::String& newName) {
}

//==============================================================================
void CircularBufferDelayAudioProcessor::prepareToPlay(double sampleRate, int samplesPerBlock) {
    auto delayTime = params.getRawParameterValue("DELAYMSRIGHT")->load();
    writePosition = std::round(getSampleRate() * delayTime / 1000.0f);
    for (int i = 0; i < 44100; i++) {
        delayBuffer.push_back(0.0f);
    }
    for (int ch = 0; ch < getTotalNumOutputChannels(); ++ch) {
        delayInMillis[ch].reset(sampleRate, 0.05f);
        feedback[ch].reset(sampleRate, 0.05f);
    }
}

void CircularBufferDelayAudioProcessor::releaseResources() {
    // When playback stops, you can use this as an opportunity to free up any
    // spare memory, etc.
}

#ifndef JucePlugin_PreferredChannelConfigurations
bool CircularBufferDelayAudioProcessor::isBusesLayoutSupported(const BusesLayout& layouts) const {
#if JucePlugin_IsMidiEffect
    juce::ignoreUnused(layouts);
    return true;
#else
    // This is the place where you check if the layout is supported.
    // In this template code we only support mono or stereo.
    // Some plugin hosts, such as certain GarageBand versions, will only
    // load plugins that support stereo bus layouts.
    if (layouts.getMainOutputChannelSet() != juce::AudioChannelSet::mono() && layouts.getMainOutputChannelSet() != juce::AudioChannelSet::stereo()) return false;

        // This checks if the input layout matches the output layout
#if !JucePlugin_IsSynth
    if (layouts.getMainOutputChannelSet() != layouts.getMainInputChannelSet()) return false;
#endif

    return true;
#endif
}
#endif

void CircularBufferDelayAudioProcessor::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());

    for (int channel = 0; channel < totalNumInputChannels; ++channel) {
        auto* channelData = buffer.getWritePointer(channel);
        for (int sample = 0; sample < buffer.getNumSamples(); ++sample) {
            delayBuffer.push_back(channelData[sample]);
        }
        for (int i = 0; i < buffer.getNumSamples(); i++) {
            auto wet = params.getRawParameterValue("DRYWET")->load();
            auto amplitude = juce::jmap(wet, 0.0f, 100.0f, 0.0f, 1.0f);
            channelData[i] += delayBuffer[i];
        }
        auto delayTimeLeft = params.getRawParameterValue("DELAYMSLEFT")->load();
        auto delayTimeRight = params.getRawParameterValue("DELAYMSRIGHT")->load();

        if (params.getRawParameterValue("DELAYLINK")->load() == true) {
            delayTimeRight = delayTimeLeft;
        }
    }
    auto bufferSize = buffer.getNumSamples();
    writePosition += bufferSize;
}

// void CircularBufferDelayAudioProcessor::fillBuffer(int sample) {
//     auto bufferSize = buffer.getNumSamples();
//     delayBuffer.copyFrom(channel, writePosition, buffer.getWritePointer(channel), bufferSize);
//     writePosition += bufferSize;
// }
//
// void CircularBufferDelayAudioProcessor::readFromBuffer(juce::AudioBuffer<float>& buffer, juce::AudioBuffer<float>& delayBuffer, int channel) {
//     auto bufferSize = buffer.getNumSamples();
//     buffer.addFrom(channel, 0, delayBuffer.getReadPointer(channel, readPosition), bufferSize);
//     readPosition += bufferSize;
// }

//==============================================================================
bool CircularBufferDelayAudioProcessor::hasEditor() const {
    return true; // (change this to false if you choose to not supply an editor)
}

juce::AudioProcessorEditor* CircularBufferDelayAudioProcessor::createEditor() {
    return new juce::GenericAudioProcessorEditor(*this);
}

//==============================================================================
void CircularBufferDelayAudioProcessor::getStateInformation(juce::MemoryBlock& destData) {
    // You should use this method to store your parameters in the memory block.
    // You could do that either as raw data, or use the XML or ValueTree classes
    // as intermediaries to make it easy to save and load complex data.
}

void CircularBufferDelayAudioProcessor::setStateInformation(const void* data, int sizeInBytes) {
    // You should use this method to restore your parameters from this memory block,
    // whose contents will have been created by the getStateInformation() call.
}

//==============================================================================
// This creates new instances of the plugin..
juce::AudioProcessor* JUCE_CALLTYPE createPluginFilter() {
    return new CircularBufferDelayAudioProcessor();
}

juce::AudioProcessorValueTreeState::ParameterLayout CircularBufferDelayAudioProcessor::createParameters() {
    std::vector<std::unique_ptr<juce::RangedAudioParameter>> params;

    params.push_back(std::make_unique<juce::AudioParameterFloat>(juce::ParameterID("DELAYMSLEFT", 1), "Delay Ms Left", 0.0f, 2000.0f, 16.0f));
    params.push_back(std::make_unique<juce::AudioParameterFloat>(juce::ParameterID("DELAYMSRIGHT", 1), "Delay Ms Right", 0.0f, 2000.0f, 16.0f));

    params.push_back(std::make_unique<juce::AudioParameterBool>(juce::ParameterID("DELAYLINK", 1), "Delay Link", false));

    params.push_back(std::make_unique<juce::AudioParameterFloat>(juce::ParameterID("FEEDBACKLEFT", 1), "Feedback Left", 0.0f, 1.0f, 0.0f));
    params.push_back(std::make_unique<juce::AudioParameterFloat>(juce::ParameterID("FEEDBACKRIGHT", 1), "Feedback Right", 0.0f, 1.0f, 0.0f));

    params.push_back(std::make_unique<juce::AudioParameterBool>(juce::ParameterID("FBLINK", 1), "Feedback Link", false));

    params.push_back(std::make_unique<juce::AudioParameterFloat>(juce::ParameterID("DRYWET", 1), "Dry/Wet", 0.0f, 100.0f, 40.0f));

    return {params.begin(), params.end()};
}

/*
  ==============================================================================

    This file contains the basic framework code for a JUCE plugin processor.

  ==============================================================================
*/

#pragma once

#include <JuceHeader.h>

//==============================================================================
/**
 */
class CircularBufferDelayAudioProcessor : public juce::AudioProcessor {
    public:
    //==============================================================================
    CircularBufferDelayAudioProcessor();
    ~CircularBufferDelayAudioProcessor() override;

    //==============================================================================
    void prepareToPlay(double sampleRate, int samplesPerBlock) override;
    void releaseResources() override;

#ifndef JucePlugin_PreferredChannelConfigurations
    bool isBusesLayoutSupported(const BusesLayout& layouts) const override;
#endif

    void processBlock(juce::AudioBuffer<float>&, juce::MidiBuffer&) override;

    //==============================================================================
    juce::AudioProcessorEditor* createEditor() override;
    bool hasEditor() const override;

    //==============================================================================
    const juce::String getName() const override;

    bool acceptsMidi() const override;
    bool producesMidi() const override;
    bool isMidiEffect() const override;
    double getTailLengthSeconds() const override;

    //==============================================================================
    int getNumPrograms() override;
    int getCurrentProgram() override;
    void setCurrentProgram(int index) override;
    const juce::String getProgramName(int index) override;
    void changeProgramName(int index, const juce::String& newName) override;

    //==============================================================================
    void getStateInformation(juce::MemoryBlock& destData) override;
    void setStateInformation(const void* data, int sizeInBytes) override;

    private:
    void fillBuffer(juce::AudioBuffer<float>& buffer, int channel);
    void feedbackBuffer(juce::AudioBuffer<float>& buffer, int channel);
    void readFromBuffer(juce::AudioBuffer<float>& buffer, juce::AudioBuffer<float>& delayBuffer, int channel);
    void updateBufferPositions(juce::AudioBuffer<float>& buffer, juce::AudioBuffer<float>& delayBuffer);

    juce::AudioProcessorValueTreeState::ParameterLayout createParameters();

    juce::AudioProcessorValueTreeState params;
    std::vector<float> delayBuffer;
    juce::LinearSmoothedValue<float> delayInMillis[2]{0.0f};
    juce::LinearSmoothedValue<float> feedback[2]{0.0f};
    int writePosition;
    int readPosition{0};

    //==============================================================================
    JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(CircularBufferDelayAudioProcessor)
};

May I ask whether you are asking for suggestions? If so, I would suggest using juce::dsp::DelayLine instead of writing your own delay.
BTW, delayBuffer.push_back(channelData[sample]) seems to be problematic as the size of delayBuffer goes to infinity (and it allocates in the real-time thread).