mirror of
https://codeberg.org/armin/chromaflock.git
synced 2026-09-01 12:20:47 +02:00
Add brickwall limiter FX section
This commit is contained in:
parent
fcdb1939ee
commit
6cd7d4f3ba
4 changed files with 161 additions and 49 deletions
44
Source/DSP/Limiter.h
Normal file
44
Source/DSP/Limiter.h
Normal file
|
|
@ -0,0 +1,44 @@
|
||||||
|
#pragma once
|
||||||
|
#include <cmath>
|
||||||
|
#include <algorithm>
|
||||||
|
|
||||||
|
class Limiter {
|
||||||
|
public:
|
||||||
|
void prepare(double sr) {
|
||||||
|
sampleRate = sr;
|
||||||
|
envelope = 0.0f;
|
||||||
|
}
|
||||||
|
|
||||||
|
void setParameters(float threshold, float ceiling, float releaseMs) {
|
||||||
|
thresholdGain = threshold;
|
||||||
|
ceilingGain = ceiling;
|
||||||
|
releaseCoeff = std::exp(-1.0f / (std::max(releaseMs, 1.0f) * 0.001f * static_cast<float>(sampleRate)));
|
||||||
|
dryWet = 1.0f;
|
||||||
|
}
|
||||||
|
|
||||||
|
float process(float input) {
|
||||||
|
float absIn = std::abs(input);
|
||||||
|
if (absIn > envelope)
|
||||||
|
envelope = absIn;
|
||||||
|
else
|
||||||
|
envelope = releaseCoeff * envelope + (1.0f - releaseCoeff) * absIn;
|
||||||
|
|
||||||
|
float target = std::max(thresholdGain, ceilingGain);
|
||||||
|
float gain = 1.0f;
|
||||||
|
if (envelope > target && envelope > 1e-6f) {
|
||||||
|
float desired = target / envelope;
|
||||||
|
gain = std::min(1.0f, desired);
|
||||||
|
}
|
||||||
|
|
||||||
|
float wet = input * gain;
|
||||||
|
return input + (wet - input) * dryWet;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
double sampleRate = 44100.0;
|
||||||
|
float thresholdGain = 1.0f;
|
||||||
|
float ceilingGain = 1.0f;
|
||||||
|
float releaseCoeff = 0.0f;
|
||||||
|
float dryWet = 1.0f;
|
||||||
|
float envelope = 0.0f;
|
||||||
|
};
|
||||||
|
|
@ -477,21 +477,25 @@ MainContentComponent::MainContentComponent(ChromaFlockProcessor& p)
|
||||||
// FX
|
// FX
|
||||||
setupCB(distTypeBox, distTypeAttach, "distType", {"Soft Clip", "Hard Clip", "Foldback", "Overdrive"});
|
setupCB(distTypeBox, distTypeAttach, "distType", {"Soft Clip", "Hard Clip", "Foldback", "Overdrive"});
|
||||||
setupParam(distAmountKnob, distAmountAttach, "distAmount", "AMOUNT");
|
setupParam(distAmountKnob, distAmountAttach, "distAmount", "AMOUNT");
|
||||||
setupParam(distMixKnob, distMixAttach, "distMix", "MIX");
|
setupParam(distSymmetryKnob, distSymmetryAttach, "distSymmetry", "SYMMETRY");
|
||||||
|
setupParam(distToneKnob, distToneAttach, "distTone", "TONE");
|
||||||
setupParam(compThresholdKnob, compThresholdAttach, "compThreshold", "THRESH");
|
setupParam(compThresholdKnob, compThresholdAttach, "compThreshold", "THRESH");
|
||||||
setupParam(compRatioKnob, compRatioAttach, "compRatio", "RATIO");
|
setupParam(compRatioKnob, compRatioAttach, "compRatio", "RATIO");
|
||||||
setupParam(compAttackKnob, compAttackAttach, "compAttack", "ATTACK");
|
setupParam(compAttackKnob, compAttackAttach, "compAttack", "ATTACK");
|
||||||
setupParam(compReleaseKnob, compReleaseAttach, "compRelease", "RELEASE");
|
setupParam(compReleaseKnob, compReleaseAttach, "compRelease", "RELEASE");
|
||||||
setupParam(compMakeupKnob, compMakeupAttach, "compMakeup", "MAKEUP");
|
setupParam(compMakeupKnob, compMakeupAttach, "compMakeup", "MAKEUP");
|
||||||
setupParam(compMixKnob, compMixAttach, "compMix", "MIX");
|
|
||||||
setupParam(autoPanRateKnob, autoPanRateAttach, "autoPanRate", "RATE");
|
setupParam(autoPanRateKnob, autoPanRateAttach, "autoPanRate", "RATE");
|
||||||
setupParam(autoPanDepthKnob, autoPanDepthAttach, "autoPanDepth", "DEPTH");
|
setupParam(autoPanDepthKnob, autoPanDepthAttach, "autoPanDepth", "DEPTH");
|
||||||
|
setupParam(autoPanPhaseKnob, autoPanPhaseAttach, "autoPanPhase", "PHASE");
|
||||||
|
setupParam(limiterThresholdKnob, limiterThresholdAttach, "limiterThreshold", "THRESH");
|
||||||
|
setupParam(limiterCeilingKnob, limiterCeilingAttach, "limiterCeiling", "CEILING");
|
||||||
|
setupParam(limiterReleaseKnob, limiterReleaseAttach, "limiterRelease", "RELEASE");
|
||||||
|
|
||||||
// LFO
|
// LFO
|
||||||
setupKnob(lfo1RateKnob, "RATE");
|
setupKnob(lfo1RateKnob, "RATE");
|
||||||
setupParam(lfo1DepthKnob, lfo1DepthAttach, "lfo1Depth", "DEPTH");
|
setupParam(lfo1DepthKnob, lfo1DepthAttach, "lfo1Depth", "DEPTH");
|
||||||
setupCB(lfo1ShapeBox, lfo1ShapeAttach, "lfo1Shape", {"Sine", "Triangle", "Saw", "Square"});
|
setupCB(lfo1ShapeBox, lfo1ShapeAttach, "lfo1Shape", {"Sine", "Triangle", "Saw", "Square"});
|
||||||
setupCB(lfo1DestBox, lfo1DestAttach, "lfo1Dest", {"Filter", "OSC1", "OSC2", "Both OSC"});
|
setupCB(lfo1DestBox, lfo1DestAttach, "lfo1Dest", {"Filter", "OSC1", "OSC2", "Both OSC", "OSC2 Phase"});
|
||||||
setupCB(lfo1SyncBox, lfo1SyncAttach, "lfo1Sync", {"Sync Off", "Sync On"});
|
setupCB(lfo1SyncBox, lfo1SyncAttach, "lfo1Sync", {"Sync Off", "Sync On"});
|
||||||
lfo1SyncBox.onChange = [this]() {
|
lfo1SyncBox.onChange = [this]() {
|
||||||
setupSyncKnob(lfo1RateKnob, lfo1RateAttach, lfo1BeatAttach, "lfo1Rate", "lfo1Beat", lfo1SyncBox);
|
setupSyncKnob(lfo1RateKnob, lfo1RateAttach, lfo1BeatAttach, "lfo1Rate", "lfo1Beat", lfo1SyncBox);
|
||||||
|
|
@ -938,9 +942,10 @@ void MainContentComponent::paint(juce::Graphics& g) {
|
||||||
g.drawText(title, x + 6, y + titlePadY, w - 12, 14, juce::Justification::centred);
|
g.drawText(title, x + 6, y + titlePadY, w - 12, 14, juce::Justification::centred);
|
||||||
};
|
};
|
||||||
|
|
||||||
drawSubSection(172, 508, 395, 140, "DISTORTION", 6);
|
drawSubSection(10, 508, 512, 140, "DISTORTION", 6);
|
||||||
drawSubSection(577, 508, 660, 140, "COMPRESSOR", 6);
|
drawSubSection(528, 508, 470, 140, "COMPRESSOR", 6);
|
||||||
drawSubSection(1247, 508, 240, 140, "AUTO-PAN", 6);
|
drawSubSection(1004, 508, 270, 140, "AUTO-PAN", 6);
|
||||||
|
drawSubSection(1280, 508, 370, 140, "LIMITER", 6);
|
||||||
|
|
||||||
// LFO sub-sections
|
// LFO sub-sections
|
||||||
drawSubSection(10, 658, 540, 150, "LFO 1", 4);
|
drawSubSection(10, 658, 540, 150, "LFO 1", 4);
|
||||||
|
|
@ -976,58 +981,94 @@ void MainContentComponent::resized() {
|
||||||
phaseOffsetKnob.setBounds(528 + (knobSize + knobSpacing) * 4, knobY2, knobSize, knobSize);
|
phaseOffsetKnob.setBounds(528 + (knobSize + knobSpacing) * 4, knobY2, knobSize, knobSize);
|
||||||
|
|
||||||
filterLabel.setBounds(1152, sectionY, 498, labelH);
|
filterLabel.setBounds(1152, sectionY, 498, labelH);
|
||||||
filterTypeBox.setBounds(1162, sectionY + labelH + 4, 200, 36);
|
filterTypeBox.setBounds(1162, sectionY + labelH + 4, 200, 28);
|
||||||
int knobYF = sectionY + labelH + comboH + 8;
|
int knobYF = sectionY + labelH + comboH + 8;
|
||||||
filterCutoffKnob.setBounds(1162, knobYF, knobSize, knobSize);
|
filterCutoffKnob.setBounds(1162, knobYF, knobSize, knobSize);
|
||||||
filterResKnob.setBounds(1162 + knobSize + knobSpacing, knobYF, knobSize, knobSize);
|
filterResKnob.setBounds(1162 + knobSize + knobSpacing, knobYF, knobSize, knobSize);
|
||||||
filterEnvAmtKnob.setBounds(1162 + (knobSize + knobSpacing) * 2, knobYF, knobSize, knobSize);
|
filterEnvAmtKnob.setBounds(1162 + (knobSize + knobSpacing) * 2, knobYF, knobSize, knobSize);
|
||||||
keyTrackKnob.setBounds(1162 + (knobSize + knobSpacing) * 3, knobYF, knobSize, knobSize);
|
keyTrackKnob.setBounds(1162 + (knobSize + knobSpacing) * 3, knobYF, knobSize, knobSize);
|
||||||
|
|
||||||
envLabel.setBounds(75, 342, 498, labelH);
|
envLabel.setBounds(10, 342, 563, labelH);
|
||||||
int knobYA = 352 + labelH + 6;
|
{
|
||||||
envAttackKnob.setBounds(85, knobYA, knobSize, knobSize);
|
int rowW = 4 * knobSize + 3 * knobSpacing;
|
||||||
envDecayKnob.setBounds(85 + knobSize + knobSpacing, knobYA, knobSize, knobSize);
|
int sx = 10 + (563 - rowW) / 2;
|
||||||
envSustainKnob.setBounds(85 + (knobSize + knobSpacing) * 2, knobYA, knobSize, knobSize);
|
int knobYA = 352 + labelH + 6;
|
||||||
envReleaseKnob.setBounds(85 + (knobSize + knobSpacing) * 3, knobYA, knobSize, knobSize);
|
envAttackKnob.setBounds(sx, knobYA, knobSize, knobSize);
|
||||||
|
envDecayKnob.setBounds(sx + (knobSize + knobSpacing), knobYA, knobSize, knobSize);
|
||||||
|
envSustainKnob.setBounds(sx + (knobSize + knobSpacing) * 2, knobYA, knobSize, knobSize);
|
||||||
|
envReleaseKnob.setBounds(sx + (knobSize + knobSpacing) * 3, knobYA, knobSize, knobSize);
|
||||||
|
}
|
||||||
|
|
||||||
fEnvLabel.setBounds(583, 342, 502, labelH);
|
fEnvLabel.setBounds(583, 342, 502, labelH);
|
||||||
int knobYFE = 352 + labelH + 6;
|
{
|
||||||
fEnvAttackKnob.setBounds(593, knobYFE, knobSize, knobSize);
|
int rowW = 4 * knobSize + 3 * knobSpacing;
|
||||||
fEnvDecayKnob.setBounds(593 + knobSize + knobSpacing, knobYFE, knobSize, knobSize);
|
int sx = 583 + (502 - rowW) / 2;
|
||||||
fEnvSustainKnob.setBounds(593 + (knobSize + knobSpacing) * 2, knobYFE, knobSize, knobSize);
|
int knobYFE = 352 + labelH + 6;
|
||||||
fEnvReleaseKnob.setBounds(593 + (knobSize + knobSpacing) * 3, knobYFE, knobSize, knobSize);
|
fEnvAttackKnob.setBounds(sx, knobYFE, knobSize, knobSize);
|
||||||
|
fEnvDecayKnob.setBounds(sx + (knobSize + knobSpacing), knobYFE, knobSize, knobSize);
|
||||||
|
fEnvSustainKnob.setBounds(sx + (knobSize + knobSpacing) * 2, knobYFE, knobSize, knobSize);
|
||||||
|
fEnvReleaseKnob.setBounds(sx + (knobSize + knobSpacing) * 3, knobYFE, knobSize, knobSize);
|
||||||
|
}
|
||||||
|
|
||||||
globalLabel.setBounds(1095, 342, 490, labelH);
|
globalLabel.setBounds(1095, 342, 555, labelH);
|
||||||
int knobYM = 352 + labelH + 6;
|
{
|
||||||
panKnob.setBounds(1105, knobYM, knobSize, knobSize);
|
int rowW = 4 * knobSize + 3 * knobSpacing;
|
||||||
driveKnob.setBounds(1105 + knobSize + knobSpacing, knobYM, knobSize, knobSize);
|
int sx = 1095 + (555 - rowW) / 2;
|
||||||
masterKnob.setBounds(1105 + (knobSize + knobSpacing) * 2, knobYM, knobSize, knobSize);
|
int knobYM = 352 + labelH + 6;
|
||||||
pbRangeKnob.setBounds(1105 + (knobSize + knobSpacing) * 3, knobYM, knobSize, knobSize);
|
panKnob.setBounds(sx, knobYM, knobSize, knobSize);
|
||||||
|
driveKnob.setBounds(sx + (knobSize + knobSpacing), knobYM, knobSize, knobSize);
|
||||||
|
masterKnob.setBounds(sx + (knobSize + knobSpacing) * 2, knobYM, knobSize, knobSize);
|
||||||
|
pbRangeKnob.setBounds(sx + (knobSize + knobSpacing) * 3, knobYM, knobSize, knobSize);
|
||||||
|
}
|
||||||
|
|
||||||
// --- FX SECTION ---
|
// --- FX SECTION ---
|
||||||
int fxY = 480;
|
int fxY = 480;
|
||||||
int fxKnobSize = 100;
|
int fxKnobSize = 80;
|
||||||
int fxLabelH = 16;
|
int fxLabelH = 16;
|
||||||
int fxKnobY = fxY + 54; // sub-section y(476) + titlePad(6) + titleH(14) + bottomPad(6) + 4
|
int fxKnobY = fxY + 54; // sub-section y(476) + titlePad(6) + titleH(14) + bottomPad(6) + 4
|
||||||
|
|
||||||
fxLabel.setBounds(10, fxY + 8, 1640, fxLabelH);
|
fxLabel.setBounds(10, fxY + 8, 1640, fxLabelH);
|
||||||
|
|
||||||
// Distortion sub-section (X=172, Y=476, W=395, H=140)
|
// Distortion sub-section (X=10, W=512) — dropdown fixed at x=17, 3 knobs fill remaining width
|
||||||
distTypeBox.setBounds(179, fxKnobY + 2, 140, 36);
|
distTypeBox.setBounds(17, fxKnobY + (fxKnobSize - 28) / 2, 140, 28);
|
||||||
distAmountKnob.setBounds(327, fxKnobY, fxKnobSize, fxKnobSize);
|
{
|
||||||
distMixKnob.setBounds(437, fxKnobY, fxKnobSize, fxKnobSize);
|
int availLeft = 17 + 140;
|
||||||
|
int availW = (10 + 512) - availLeft;
|
||||||
|
int rowW = 3 * fxKnobSize + 2 * 6;
|
||||||
|
int dx = availLeft + (availW - rowW) / 2;
|
||||||
|
distAmountKnob.setBounds(dx, fxKnobY, fxKnobSize, fxKnobSize);
|
||||||
|
distSymmetryKnob.setBounds(dx + (fxKnobSize + 6), fxKnobY, fxKnobSize, fxKnobSize);
|
||||||
|
distToneKnob.setBounds(dx + (fxKnobSize + 6) * 2, fxKnobY, fxKnobSize, fxKnobSize);
|
||||||
|
}
|
||||||
|
|
||||||
// Compressor sub-section (X=577, Y=476, W=660, H=140)
|
// Compressor sub-section (X=528, W=470)
|
||||||
compThresholdKnob.setBounds(585, fxKnobY, fxKnobSize, fxKnobSize);
|
{
|
||||||
compRatioKnob.setBounds(695, fxKnobY, fxKnobSize, fxKnobSize);
|
int rowW = 5 * fxKnobSize + 4 * 6;
|
||||||
compAttackKnob.setBounds(805, fxKnobY, fxKnobSize, fxKnobSize);
|
int sx = 528 + (470 - rowW) / 2;
|
||||||
compReleaseKnob.setBounds(915, fxKnobY, fxKnobSize, fxKnobSize);
|
compThresholdKnob.setBounds(sx, fxKnobY, fxKnobSize, fxKnobSize);
|
||||||
compMakeupKnob.setBounds(1025, fxKnobY, fxKnobSize, fxKnobSize);
|
compRatioKnob.setBounds(sx + (fxKnobSize + 6), fxKnobY, fxKnobSize, fxKnobSize);
|
||||||
compMixKnob.setBounds(1135, fxKnobY, fxKnobSize, fxKnobSize);
|
compAttackKnob.setBounds(sx + (fxKnobSize + 6) * 2, fxKnobY, fxKnobSize, fxKnobSize);
|
||||||
|
compReleaseKnob.setBounds(sx + (fxKnobSize + 6) * 3, fxKnobY, fxKnobSize, fxKnobSize);
|
||||||
|
compMakeupKnob.setBounds(sx + (fxKnobSize + 6) * 4, fxKnobY, fxKnobSize, fxKnobSize);
|
||||||
|
}
|
||||||
|
|
||||||
// Auto-Pan sub-section (X=1247, Y=476, W=240, H=140)
|
// Auto-Pan sub-section (X=1004, W=270)
|
||||||
autoPanRateKnob.setBounds(1255, fxKnobY, fxKnobSize, fxKnobSize);
|
{
|
||||||
autoPanDepthKnob.setBounds(1255 + fxKnobSize + 8, fxKnobY, fxKnobSize, fxKnobSize);
|
int rowW = 3 * fxKnobSize + 2 * 6;
|
||||||
|
int ax = 1004 + (270 - rowW) / 2;
|
||||||
|
autoPanRateKnob.setBounds(ax, fxKnobY, fxKnobSize, fxKnobSize);
|
||||||
|
autoPanDepthKnob.setBounds(ax + (fxKnobSize + 6), fxKnobY, fxKnobSize, fxKnobSize);
|
||||||
|
autoPanPhaseKnob.setBounds(ax + (fxKnobSize + 6) * 2, fxKnobY, fxKnobSize, fxKnobSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Limiter sub-section (X=1280, W=370)
|
||||||
|
{
|
||||||
|
int rowW = 3 * fxKnobSize + 2 * 6;
|
||||||
|
int lx = 1280 + (370 - rowW) / 2;
|
||||||
|
limiterThresholdKnob.setBounds(lx, fxKnobY, fxKnobSize, fxKnobSize);
|
||||||
|
limiterCeilingKnob.setBounds(lx + (fxKnobSize + 6), fxKnobY, fxKnobSize, fxKnobSize);
|
||||||
|
limiterReleaseKnob.setBounds(lx + (fxKnobSize + 6) * 2, fxKnobY, fxKnobSize, fxKnobSize);
|
||||||
|
}
|
||||||
|
|
||||||
// --- LFO SECTION ---
|
// --- LFO SECTION ---
|
||||||
int lfoY = 638;
|
int lfoY = 638;
|
||||||
|
|
|
||||||
|
|
@ -143,9 +143,10 @@ private:
|
||||||
|
|
||||||
// FX controls
|
// FX controls
|
||||||
juce::ComboBox distTypeBox;
|
juce::ComboBox distTypeBox;
|
||||||
juce::Slider distAmountKnob, distMixKnob;
|
juce::Slider distAmountKnob, distSymmetryKnob, distToneKnob;
|
||||||
juce::Slider compThresholdKnob, compRatioKnob, compAttackKnob, compReleaseKnob, compMakeupKnob, compMixKnob;
|
juce::Slider compThresholdKnob, compRatioKnob, compAttackKnob, compReleaseKnob, compMakeupKnob;
|
||||||
juce::Slider autoPanRateKnob, autoPanDepthKnob;
|
juce::Slider autoPanRateKnob, autoPanDepthKnob, autoPanPhaseKnob;
|
||||||
|
juce::Slider limiterThresholdKnob, limiterCeilingKnob, limiterReleaseKnob;
|
||||||
|
|
||||||
// LFO controls
|
// LFO controls
|
||||||
juce::Slider lfo1RateKnob, lfo1DepthKnob;
|
juce::Slider lfo1RateKnob, lfo1DepthKnob;
|
||||||
|
|
@ -181,10 +182,11 @@ private:
|
||||||
|
|
||||||
// FX attachments
|
// FX attachments
|
||||||
std::unique_ptr<ComboBoxAttachment> distTypeAttach;
|
std::unique_ptr<ComboBoxAttachment> distTypeAttach;
|
||||||
std::unique_ptr<SliderAttachment> distAmountAttach, distMixAttach;
|
std::unique_ptr<SliderAttachment> distAmountAttach, distSymmetryAttach, distToneAttach;
|
||||||
std::unique_ptr<SliderAttachment> compThresholdAttach, compRatioAttach, compAttackAttach;
|
std::unique_ptr<SliderAttachment> compThresholdAttach, compRatioAttach, compAttackAttach;
|
||||||
std::unique_ptr<SliderAttachment> compReleaseAttach, compMakeupAttach, compMixAttach;
|
std::unique_ptr<SliderAttachment> compReleaseAttach, compMakeupAttach;
|
||||||
std::unique_ptr<SliderAttachment> autoPanRateAttach, autoPanDepthAttach;
|
std::unique_ptr<SliderAttachment> autoPanRateAttach, autoPanDepthAttach, autoPanPhaseAttach;
|
||||||
|
std::unique_ptr<SliderAttachment> limiterThresholdAttach, limiterCeilingAttach, limiterReleaseAttach;
|
||||||
|
|
||||||
// LFO attachments
|
// LFO attachments
|
||||||
std::unique_ptr<SliderAttachment> lfo1RateAttach, lfo1DepthAttach, lfo1BeatAttach;
|
std::unique_ptr<SliderAttachment> lfo1RateAttach, lfo1DepthAttach, lfo1BeatAttach;
|
||||||
|
|
|
||||||
|
|
@ -180,6 +180,19 @@ juce::AudioProcessorValueTreeState::ParameterLayout ChromaFlockProcessor::create
|
||||||
juce::NormalisableRange<float>(0.05f, 20.0f, 0.01f, 0.4f), 2.0f));
|
juce::NormalisableRange<float>(0.05f, 20.0f, 0.01f, 0.4f), 2.0f));
|
||||||
layout.add(std::make_unique<juce::AudioParameterFloat>(
|
layout.add(std::make_unique<juce::AudioParameterFloat>(
|
||||||
juce::ParameterID{"autoPanDepth", 1}, "Pan Depth", zeroOne, 0.0f));
|
juce::ParameterID{"autoPanDepth", 1}, "Pan Depth", zeroOne, 0.0f));
|
||||||
|
layout.add(std::make_unique<juce::AudioParameterFloat>(
|
||||||
|
juce::ParameterID{"autoPanPhase", 1}, "Pan Phase", zeroOne, 0.0f));
|
||||||
|
|
||||||
|
// LIMITER
|
||||||
|
layout.add(std::make_unique<juce::AudioParameterFloat>(
|
||||||
|
juce::ParameterID{"limiterThreshold", 1}, "Limiter Threshold",
|
||||||
|
juce::NormalisableRange<float>(0.0f, 1.0f, 0.001f, 0.3f), 1.0f));
|
||||||
|
layout.add(std::make_unique<juce::AudioParameterFloat>(
|
||||||
|
juce::ParameterID{"limiterCeiling", 1}, "Limiter Ceiling",
|
||||||
|
juce::NormalisableRange<float>(0.0f, 1.0f, 0.001f, 0.3f), 0.9f));
|
||||||
|
layout.add(std::make_unique<juce::AudioParameterFloat>(
|
||||||
|
juce::ParameterID{"limiterRelease", 1}, "Limiter Release",
|
||||||
|
juce::NormalisableRange<float>(1.0f, 500.0f, 0.1f), 50.0f));
|
||||||
|
|
||||||
// LFO 1
|
// LFO 1
|
||||||
layout.add(std::make_unique<juce::AudioParameterFloat>(
|
layout.add(std::make_unique<juce::AudioParameterFloat>(
|
||||||
|
|
@ -256,6 +269,7 @@ void ChromaFlockProcessor::prepareToPlay(double sampleRate, int samplesPerBlock)
|
||||||
synth.setCurrentPlaybackSampleRate(sampleRate);
|
synth.setCurrentPlaybackSampleRate(sampleRate);
|
||||||
distortion.prepare(sampleRate);
|
distortion.prepare(sampleRate);
|
||||||
compressor.prepare(sampleRate);
|
compressor.prepare(sampleRate);
|
||||||
|
limiter.prepare(sampleRate);
|
||||||
delay.prepare(sampleRate);
|
delay.prepare(sampleRate);
|
||||||
reverbFX.prepare(sampleRate);
|
reverbFX.prepare(sampleRate);
|
||||||
autoPanPhase = 0.0f;
|
autoPanPhase = 0.0f;
|
||||||
|
|
@ -402,6 +416,13 @@ void ChromaFlockProcessor::processBlock(juce::AudioBuffer<float>& buffer, juce::
|
||||||
}
|
}
|
||||||
delay.setParameters(delayTimeMs, getParam("delayFeedback"), getParam("delayMix"));
|
delay.setParameters(delayTimeMs, getParam("delayFeedback"), getParam("delayMix"));
|
||||||
reverbFX.setParameters(getParam("reverbSize"), getParam("reverbDamping"), 0.8f, getParam("reverbMix"));
|
reverbFX.setParameters(getParam("reverbSize"), getParam("reverbDamping"), 0.8f, getParam("reverbMix"));
|
||||||
|
limiter.setParameters(getParam("limiterThreshold"), getParam("limiterCeiling"),
|
||||||
|
getParam("limiterRelease"));
|
||||||
|
|
||||||
|
// Master fader applies a natural (perceptual) taper and is the final gain
|
||||||
|
// stage, so it always controls output level regardless of the limiter.
|
||||||
|
float masterValue = getParam("masterLevel");
|
||||||
|
float masterGain = masterValue * masterValue;
|
||||||
|
|
||||||
// FX processing: distortion → compression → auto-pan
|
// FX processing: distortion → compression → auto-pan
|
||||||
int numSamples = buffer.getNumSamples();
|
int numSamples = buffer.getNumSamples();
|
||||||
|
|
@ -421,9 +442,13 @@ void ChromaFlockProcessor::processBlock(juce::AudioBuffer<float>& buffer, juce::
|
||||||
left = compressor.process(left);
|
left = compressor.process(left);
|
||||||
right = compressor.process(right);
|
right = compressor.process(right);
|
||||||
|
|
||||||
|
// Brickwall limiter (stereo)
|
||||||
|
left = limiter.process(left);
|
||||||
|
right = limiter.process(right);
|
||||||
|
|
||||||
// Auto-pan
|
// Auto-pan
|
||||||
if (panDepth > 0.001f) {
|
if (panDepth > 0.001f) {
|
||||||
float panLfo = std::sin(autoPanPhase * twoPi);
|
float panLfo = std::sin((autoPanPhase + autoPanPhaseOffset) * twoPi);
|
||||||
float leftGain = 1.0f - panDepth * 0.5f * (1.0f + panLfo);
|
float leftGain = 1.0f - panDepth * 0.5f * (1.0f + panLfo);
|
||||||
float rightGain = 1.0f - panDepth * 0.5f * (1.0f - panLfo);
|
float rightGain = 1.0f - panDepth * 0.5f * (1.0f - panLfo);
|
||||||
left *= leftGain;
|
left *= leftGain;
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue