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)
};