This commit is contained in:
Armin 2026-07-23 23:40:48 +02:00
commit d21bc831e1
178 changed files with 24136 additions and 0 deletions

View file

@ -0,0 +1,98 @@
/*
==============================================================================
BandEQ.h
Created: 2 Jan 2024 5:02:27pm
Author: binya
==============================================================================
*/
#pragma once
#include <JuceHeader.h>
#include "Effect.h"
/** This is a sample 3-band EQ effect, inspired by the Kiloheart's free 3-Band EQ plugin. */
class BandEQ final : public Effect
{
public:
void initialize(int numChannels, int fxSampleRate) override
{
sampleRate = fxSampleRate;
juce::dsp::ProcessSpec spec{};
spec.numChannels = numChannels;
spec.sampleRate = sampleRate;
filterChain.reset();
filterChain.prepare(spec);
}
void updateParams(float lowFreq, float highFreq, float lowGain, float midGain, float highGain)
{
// This possibly has an issue where the frequencies are outside of range on plugin initialization
auto coeffLow = juce::dsp::IIR::Coefficients<float>::makeLowShelf(sampleRate, lowFreq, Q, juce::Decibels::decibelsToGain(lowGain));
auto coeffMid1 = juce::dsp::IIR::Coefficients<float>::makeHighShelf(sampleRate, lowFreq, Q, juce::Decibels::decibelsToGain(midGain));
auto coeffMid2 = juce::dsp::IIR::Coefficients<float>::makeHighShelf(sampleRate, highFreq, Q, juce::Decibels::decibelsToGain(-midGain));
auto coeffHigh = juce::dsp::IIR::Coefficients<float>::makeHighShelf(sampleRate, highFreq, Q, juce::Decibels::decibelsToGain(highGain));
*filterChain.get<0>().state = *coeffLow;
*filterChain.get<1>().state = *coeffMid1;
*filterChain.get<2>().state = *coeffMid2;
*filterChain.get<3>().state = *coeffHigh;
}
void updateParams(const SamplerParameters& samplerSound, bool) override
{
updateParams(
samplerSound.eqLowFreq->get(), samplerSound.eqHighFreq->get(),
samplerSound.eqLowGain->get(), samplerSound.eqMidGain->get(), samplerSound.eqHighGain->get()
);
}
void process(juce::AudioBuffer<float>& buffer, int numSamples, int startSample = 0) override
{
juce::dsp::AudioBlock block{ buffer.getArrayOfWritePointers(), size_t(buffer.getNumChannels()), size_t(startSample), size_t(numSamples) };
juce::dsp::ProcessContextReplacing context{ block };
filterChain.process(context);
}
juce::Array<double> getMagnitudeForFrequencyArray(juce::Array<double> frequencies)
{
std::array filters{
&filterChain.get<0>(),
&filterChain.get<1>(),
&filterChain.get<2>(),
&filterChain.get<3>()
};
juce::AudioBuffer<double> magnitudes{1, frequencies.size()};
magnitudes.clear();
bool empty{ true };
for (const auto& filter : filters)
{
juce::AudioBuffer<double> temp{ 1, frequencies.size()};
filter->state->getMagnitudeForFrequencyArray(frequencies.getRawDataPointer(), temp.getWritePointer(0), frequencies.size(), 48000);
if (empty)
{
magnitudes.addFrom(0, 0, temp.getReadPointer(0), frequencies.size());
empty = false;
}
else
{
for (int i = 0; i < temp.getNumSamples(); i++)
magnitudes.setSample(0, i, magnitudes.getSample(0, i) * temp.getSample(0, i));
}
}
return juce::Array<double>{magnitudes.getReadPointer(0), frequencies.size()};
}
private:
using Filter = juce::dsp::ProcessorDuplicator<juce::dsp::IIR::Filter<float>, juce::dsp::IIR::Coefficients<float>>;
using FilterChain = juce::dsp::ProcessorChain<Filter, Filter, Filter, Filter>;
static constexpr float Q{ 0.6f }; // Magic number I saw online for the response curve
int sampleRate{ 0 };
FilterChain filterChain;
};

View file

@ -0,0 +1,57 @@
/*
==============================================================================
Chorus.h
Created: 4 Jan 2024 7:22:36pm
Author: binya
==============================================================================
*/
#pragma once
#include <JuceHeader.h>
#include "Effect.h"
/** This is a simple wrapper around the JUCE Chorus class */
class Chorus final : public Effect
{
public:
explicit Chorus(int expectedBlockSize=MAX_BLOCK_SIZE) : expectedBlockSize(expectedBlockSize) {}
void initialize(int numChannels, int fxSampleRate) override
{
juce::dsp::ProcessSpec processSpec{ double(fxSampleRate), juce::uint32(expectedBlockSize), juce::uint32(numChannels) };
chorus.reset();
chorus.prepare(processSpec);
}
void updateParams(const SamplerParameters& sampleSound, bool realtime) override
{
chorus.setRate(sampleSound.chorusRate->get());
chorus.setDepth(sampleSound.chorusDepth->get());
chorus.setFeedback(sampleSound.chorusFeedback->get());
if (!realtime) // Center delay cannot be modulated
chorus.setCentreDelay(juce::jmin<float>(sampleSound.chorusCenterDelay->get(), 99.9f));
chorus.setMix(sampleSound.chorusMix->get());
}
void process(juce::AudioBuffer<float>& buffer, int numSamples, int startSample = 0) override
{
while (numSamples > 0)
{
juce::dsp::AudioBlock<float> block{ buffer.getArrayOfWritePointers(), size_t(buffer.getNumChannels()), size_t(startSample), size_t(juce::jmin(MAX_BLOCK_SIZE, numSamples)) };
juce::dsp::ProcessContextReplacing<float> context{ block };
chorus.process(context);
startSample += MAX_BLOCK_SIZE;
numSamples -= MAX_BLOCK_SIZE;
}
}
private:
static constexpr int MAX_BLOCK_SIZE{ 1024 };
juce::dsp::Chorus<float> chorus{};
int expectedBlockSize;
};

View file

@ -0,0 +1,73 @@
/*
==============================================================================
Distortion.h
Created: 30 Dec 2023 8:39:04pm
Author: binya
==============================================================================
*/
#pragma once
#include <JuceHeader.h>
#include "Effect.h"
#include <gin_distortion.h>
/** This is a simple Distortion class that wraps around gin::AirWindowsDistortion */
class Distortion final : public Effect
{
public:
void initialize(int numChannels, int fxSampleRate) override
{
int numEffects = numChannels / 2 + numChannels % 2;
channelDistortions.resize(numEffects);
for (int ch = 0; ch < numEffects; ch++)
{
channelDistortions[ch] = std::make_unique<gin::AirWindowsDistortion>();
channelDistortions[ch]->setSampleRate(fxSampleRate);
}
}
void updateParams(float density, float highpass, float mix)
{
float mappedDensity = density >= 0.f ? juce::jmap<float>(density, 0.2f, 1.f) : juce::jmap<float>(density, -0.5f, 0.f, 0.f, 0.2f);
// We try to keep the gain of the distortion constant:
// This is a sigmoid function found manually from graphing the output of the distortion. When the mapped density < 0.2f,
// a different function needs to be used, since the distortion actually behaves differently according to that threshold.
// Note that the gain parameter only applies if it's less than 1.f, so we need to increase the gain ourselves after processing.
float gainChange = mappedDensity >= 0.2f ? (0.2f * (1.f + expf(-7.f * (mappedDensity - 0.5f)))) : 1.f;
postGain = mappedDensity < 0.2f ? 1.f / (4.f * mappedDensity + 0.2f) : 1.f;
for (const auto& channelDistortion : channelDistortions)
channelDistortion->setParams(mappedDensity, highpass, gainChange, mix);
}
void updateParams(const SamplerParameters& sampleSound, bool) override
{
updateParams(
sampleSound.distortionDensity->get(),
sampleSound.distortionHighpass->get(),
sampleSound.distortionMix->get()
);
}
void process(juce::AudioBuffer<float>& buffer, int numSamples, int startSample=0) override
{
bool lastIsMono = buffer.getNumChannels() % 2 == 1;
for (int ch = 0; ch < buffer.getNumChannels(); ch += 2)
{
// Note that I modified the gin header to make this more straightforward
if (lastIsMono && ch == buffer.getNumChannels() - 1)
channelDistortions[ch / 2]->process(buffer.getWritePointer(ch, startSample), buffer.getWritePointer(ch, startSample), numSamples);
else
channelDistortions[ch / 2]->process(buffer.getWritePointer(ch, startSample), buffer.getWritePointer(ch + 1, startSample), numSamples);
}
buffer.applyGain(startSample, numSamples, postGain);
}
private:
std::vector<std::unique_ptr<gin::AirWindowsDistortion>> channelDistortions;
float postGain{ 0. };
};

View file

@ -0,0 +1,23 @@
/*
==============================================================================
Effect.h
Created: 30 Dec 2023 8:35:28pm
Author: binya
==============================================================================
*/
#pragma once
#include <JuceHeader.h>
#include "../SamplerParameters.h"
class Effect
{
public:
virtual ~Effect() = default;
virtual void initialize(int numChannels, int fxSampleRate) = 0;
virtual void updateParams(const SamplerParameters& sampleSound, bool modulating = false) = 0;
virtual void process(juce::AudioBuffer<float>& buffer, int numSamples, int startSample=0) = 0;
};

View file

@ -0,0 +1,85 @@
/*
==============================================================================
Reverb.h
Created: 30 Dec 2023 8:38:55pm
Author: binya
==============================================================================
*/
#pragma once
#include <JuceHeader.h>
#include "Effect.h"
#include <gin_simpleverb.h>
/** This is a simple Effect class that wraps around Gin's SimpleVerb implementation */
class Reverb final : public Effect
{
public:
Reverb() = default;
void initialize(int numChannels, int fxSampleRate) override
{
int numEffects = numChannels / 2 + numChannels % 2;
channelGinReverbs.resize(numEffects);
for (int ch = 0; ch < numEffects; ch++)
{
channelGinReverbs[ch] = std::make_unique<gin::SimpleVerb>();
channelGinReverbs[ch]->setSampleRate(float(fxSampleRate));
channelGinReverbs[ch]->setParameters(0.f, 0.f, 1.f, 0.f, 1.f, 0.f, 0.f);
}
}
// The intended ranges of these values are in PluginParameters.h
void updateParams(float size, float damping, float predelay, float lows, float highs, float mix) const
{
for (const auto& channelGinReverb : channelGinReverbs)
{
channelGinReverb->setParameters(
juce::jmap<float>(size, PluginParameters::REVERB_SIZE_RANGE.getStart(), PluginParameters::REVERB_SIZE_RANGE.getEnd(), 0.f, 1.f),
juce::jmap<float>(damping, PluginParameters::REVERB_DAMPING_RANGE.getStart(), PluginParameters::REVERB_DAMPING_RANGE.getEnd(), 0.f, 1.f),
float(sqrtf(predelay / 250.f)), // conversions to counteract the faders in this algorithm
juce::jmap<float>(highs, PluginParameters::REVERB_HIGHS_RANGE.getStart(), PluginParameters::REVERB_HIGHS_RANGE.getEnd(), 0.3f, 1.f),
1.f - juce::jmap<float>(lows, PluginParameters::REVERB_LOWS_RANGE.getStart(), PluginParameters::REVERB_LOWS_RANGE.getEnd(), 0.3f, 1.f),
mix,
(1.f - mix) / 2.f
);
}
}
void updateParams(const SamplerParameters& sampleSound, bool) override
{
updateParams(
sampleSound.reverbSize->get(),
sampleSound.reverbDamping->get(),
sampleSound.reverbPredelay->get(),
sampleSound.reverbLows->get(),
sampleSound.reverbHighs->get(),
sampleSound.reverbMix->get()
);
}
void process(juce::AudioBuffer<float>& buffer, int numSamples, int startSample = 0) override
{
bool lastIsMono = buffer.getNumChannels() % 2 == 1;
for (int ch = 0; ch < buffer.getNumChannels(); ch += 2)
{
// I modified the header of the process method to work easier with this code, you'll need to do the same to get it to compile
// void SimpleVerb::process (const float* in1, const float* in2, float* out1, float* out2, int numSamples)
if (lastIsMono && ch == buffer.getNumChannels() - 1)
channelGinReverbs[ch / 2]->process(
buffer.getReadPointer(ch, startSample), buffer.getReadPointer(ch, startSample),
buffer.getWritePointer(ch, startSample), buffer.getWritePointer(ch, startSample), numSamples);
else
channelGinReverbs[ch / 2]->process(
buffer.getReadPointer(ch, startSample), buffer.getReadPointer(ch + 1, startSample),
buffer.getWritePointer(ch, startSample), buffer.getWritePointer(ch + 1, startSample), numSamples);
}
}
private:
std::vector<std::unique_ptr<gin::SimpleVerb>> channelGinReverbs;
};