UI polish: amber theme, smooth spectrum, MIDI LED, VU tuning

- Rename section labels: COMPRESSOR → COMPRESSION, AUTO-PAN → PANNING
- Replace spectrum analyzer bars with smooth gapless filled wave
- Use gold (#c59c07) for wave/spectrum strokes and piano roll active keys
- Amber CRT-style patch LCD with dark background and gold dot-matrix text
- Add MIDI signal LED with glow between semi and scale controls
- Reduce VU meter sensitivity (5x → 1.5x) and match amber gradient theme
- Add subtle glow behind section and sub-section boxes
- Fix Compressor.h class name and PluginProcessor include to match
This commit is contained in:
Armin 2026-07-16 02:31:34 +02:00
commit 628e1e3cba
6 changed files with 120 additions and 41 deletions

View file

@ -21,7 +21,7 @@ will work reliably, be useful in any way, or even make any sense whatsoever.
## Signal Flow ## Signal Flow
``` ```
Osc1 + Osc2 → Amp Envelope × Velocity → Filter → Drive → Distortion → Compressor → Auto-Pan → Delay → Reverb → Output Osc1 + Osc2 → Amp Envelope × Velocity → Filter → Drive → Distortion → Compression → Auto-Pan → Delay → Reverb → Output
``` ```
## Oscillators ## Oscillators
@ -81,7 +81,7 @@ All effects are stereo and processed in series after the synth voice.
- **Mix** (dry/wet): 0.0 1.0 - **Mix** (dry/wet): 0.0 1.0
- Bypassed when amount < 0.001 - Bypassed when amount < 0.001
### Compressor ### Compression
- **Threshold**: -60 dB 0 dB - **Threshold**: -60 dB 0 dB
- **Ratio**: 1:1 20:1 - **Ratio**: 1:1 20:1
@ -165,7 +165,7 @@ Custom dark theme with gold accents. ~50 knobs across 9 sections.
| FILTER ENV | Attack, Decay, Sustain, Release knobs | | FILTER ENV | Attack, Decay, Sustain, Release knobs |
| Master | Pan, Drive, Level, PB Range knobs | | Master | Pan, Drive, Level, PB Range knobs |
| Distortion | Type selector, Amount, Mix knobs | | Distortion | Type selector, Amount, Mix knobs |
| Compressor | Threshold, Ratio, Attack, Release, Makeup, Mix knobs | | Compression | Threshold, Ratio, Attack, Release, Makeup, Mix knobs |
| Auto-Pan | Rate, Depth knobs | | Auto-Pan | Rate, Depth knobs |
| LFO 1/2 | Rate, Depth knobs, Shape and Destination selectors | | LFO 1/2 | Rate, Depth knobs, Shape and Destination selectors |
| Delay | Time, Feedback, Mix knobs, Ping-Pong selector, Spread knob | | Delay | Time, Feedback, Mix knobs, Ping-Pong selector, Spread knob |

View file

@ -2,7 +2,7 @@
#include <cmath> #include <cmath>
#include <algorithm> #include <algorithm>
class Compressor { class Compression {
public: public:
void prepare(double sr) { void prepare(double sr) {
sampleRate = sr; sampleRate = sr;

View file

@ -168,7 +168,7 @@ void VuMeter::paint(juce::Graphics& g) {
clipPath.addRoundedRectangle(bounds, 9.0f); clipPath.addRoundedRectangle(bounds, 9.0f);
g.reduceClipRegion(clipPath); g.reduceClipRegion(clipPath);
juce::ColourGradient grad(juce::Colour(0xff00cc44).withAlpha(0.7f), 0.0f, bounds.getBottom(), juce::ColourGradient grad(juce::Colour(0xffc59c07).withAlpha(0.7f), 0.0f, bounds.getBottom(),
juce::Colour(0xffcc2200).withAlpha(0.7f), 0.0f, bounds.getY(), false); juce::Colour(0xffcc2200).withAlpha(0.7f), 0.0f, bounds.getY(), false);
g.setGradientFill(grad); g.setGradientFill(grad);
g.fillRoundedRectangle(innerBar, 2.0f); g.fillRoundedRectangle(innerBar, 2.0f);
@ -230,13 +230,13 @@ void WaveformDisplay::paint(juce::Graphics& g) {
clipPath.addRoundedRectangle(bounds, 9.0f); clipPath.addRoundedRectangle(bounds, 9.0f);
g.reduceClipRegion(clipPath); g.reduceClipRegion(clipPath);
juce::ColourGradient waveGrad(juce::Colour(0xff00ff88).withAlpha(0.5f), 0.0f, bounds.getY(), juce::ColourGradient waveGrad(juce::Colour(0xffc59c07).withAlpha(0.5f), 0.0f, bounds.getY(),
juce::Colour(0xff005522).withAlpha(0.4f), 0.0f, bounds.getBottom(), false); juce::Colour(0xff3d2e02).withAlpha(0.4f), 0.0f, bounds.getBottom(), false);
g.setGradientFill(waveGrad); g.setGradientFill(waveGrad);
g.fillPath(filledPath); g.fillPath(filledPath);
g.setColour(juce::Colour(0xff00ff88).withAlpha(0.7f)); g.setColour(juce::Colour(0xffc59c07).withAlpha(0.9f));
g.strokePath(wavePath, juce::PathStrokeType(0.7f)); g.strokePath(wavePath, juce::PathStrokeType(1.5f));
} }
g.setColour(juce::Colour(0xffccaa44)); g.setColour(juce::Colour(0xffccaa44));
@ -272,23 +272,26 @@ void SpectrumAnalyzer::paint(juce::Graphics& g) {
fft->performFrequencyOnlyForwardTransform(fftData.data()); fft->performFrequencyOnlyForwardTransform(fftData.data());
int numBars = 48; int numPoints = 128;
float w = bounds.getWidth(); float w = bounds.getWidth();
float h = bounds.getHeight() - 22.0f; float h = bounds.getHeight() - 22.0f;
float barBottom = bounds.getBottom() - 2.0f; float bottom = bounds.getBottom() - 2.0f;
float barW = w / static_cast<float>(numBars);
int maxBin = fftSize / 4; int maxBin = fftSize / 4;
float sampleRate = static_cast<float>(processor.getSampleRate());
float binHz = sampleRate / static_cast<float>(fftSize);
float lowCut = 80.0f;
float lowPass = 250.0f;
juce::Graphics::ScopedSaveState savedClip(g); juce::Graphics::ScopedSaveState savedClip(g);
juce::Path clipPath; juce::Path clipPath;
clipPath.addRoundedRectangle(bounds, 9.0f); clipPath.addRoundedRectangle(bounds, 9.0f);
g.reduceClipRegion(clipPath); g.reduceClipRegion(clipPath);
for (int bar = 0; bar < numBars; ++bar) { std::vector<float> mags(numPoints + 1);
float t = static_cast<float>(bar) / static_cast<float>(numBars); for (int i = 0; i <= numPoints; ++i) {
float tNext = static_cast<float>(bar + 1) / static_cast<float>(numBars); float t = static_cast<float>(i) / static_cast<float>(numPoints);
int binStart = static_cast<int>(std::pow(t, 2.0f) * static_cast<float>(maxBin)); int binStart = static_cast<int>(std::pow(t, 2.0f) * static_cast<float>(maxBin));
int binEnd = static_cast<int>(std::pow(tNext, 2.0f) * static_cast<float>(maxBin)); int binEnd = static_cast<int>(std::pow(t + 1.0f / static_cast<float>(numPoints), 2.0f) * static_cast<float>(maxBin));
if (binEnd <= binStart) binEnd = binStart + 1; if (binEnd <= binStart) binEnd = binStart + 1;
if (binEnd > maxBin) binEnd = maxBin; if (binEnd > maxBin) binEnd = maxBin;
@ -301,24 +304,43 @@ void SpectrumAnalyzer::paint(juce::Graphics& g) {
mag = count > 0 ? mag / static_cast<float>(count) : 0.0f; mag = count > 0 ? mag / static_cast<float>(count) : 0.0f;
mag = mag / static_cast<float>(fftSize); mag = mag / static_cast<float>(fftSize);
mag = std::sqrt(mag) * 6.0f; mag = std::sqrt(mag) * 6.0f;
mag = juce::jlimit(0.0f, 1.0f, mag);
float barH = mag * h; float centerHz = static_cast<float>((binStart + binEnd) / 2) * binHz;
float x = bounds.getX() + static_cast<float>(bar) * barW; float rolloff = juce::jlimit(0.0f, 1.0f, (centerHz - lowCut) / (lowPass - lowCut));
float bw = barW - 2.0f; mag *= rolloff;
float bx = x + 1.0f; mags[i] = juce::jlimit(0.0f, 1.0f, mag);
float by = barBottom - barH;
g.setColour(juce::Colour(0xff00ff88).withAlpha(0.4f));
g.fillRect(bx, by, bw, barH);
} }
juce::Path wavePath;
wavePath.startNewSubPath(bounds.getX(), bottom);
juce::Path strokePath;
strokePath.startNewSubPath(bounds.getX(), bottom - mags[0] * h);
for (int i = 0; i <= numPoints; ++i) {
float t = static_cast<float>(i) / static_cast<float>(numPoints);
float x = bounds.getX() + t * w;
float y = bottom - mags[i] * h;
wavePath.lineTo(x, y);
if (i > 0) strokePath.lineTo(x, y);
}
wavePath.lineTo(bounds.getRight(), bottom);
wavePath.closeSubPath();
juce::ColourGradient fillGrad(juce::Colour(0xffc59c07).withAlpha(0.35f), 0.0f, bottom,
juce::Colour(0xffc59c07).withAlpha(0.02f), 0.0f, bottom - h, false);
g.setGradientFill(fillGrad);
g.fillPath(wavePath);
g.setColour(juce::Colour(0xffc59c07).withAlpha(0.9f));
g.strokePath(strokePath, juce::PathStrokeType(1.5f));
g.setColour(juce::Colour(0xffccaa44)); g.setColour(juce::Colour(0xffccaa44));
g.setFont(juce::Font(13.0f).boldened()); g.setFont(juce::Font(13.0f).boldened());
g.drawText("SPECTRUM", bounds.withBottom(bounds.getY() + 22.0f).translated(0, 6), juce::Justification::centred); g.drawText("SPECTRUM", bounds.withBottom(bounds.getY() + 22.0f).translated(0, 6), juce::Justification::centred);
} }
// --- Patch LCD (green dot-matrix) --- // --- Patch LCD (amber dot-matrix) ---
namespace { namespace {
struct Glyph { char c; unsigned char rows[7]; }; struct Glyph { char c; unsigned char rows[7]; };
// 5x7 font; each byte is a row, bits 4..0 are columns left->right. // 5x7 font; each byte is a row, bits 4..0 are columns left->right.
@ -374,16 +396,16 @@ void PatchLCD::paint(juce::Graphics& g) {
auto b = getLocalBounds(); auto b = getLocalBounds();
// Dark LCD panel // Dark LCD panel
g.setColour(juce::Colour(0xcc061206)); g.setColour(juce::Colour(0xcc1a0e00));
g.fillRoundedRectangle(b.toFloat(), 3.0f); g.fillRoundedRectangle(b.toFloat(), 3.0f);
g.setColour(juce::Colour(0xff114411)); g.setColour(juce::Colour(0xff5a3a00));
g.drawRoundedRectangle(b.toFloat(), 3.0f, 1.0f); g.drawRoundedRectangle(b.toFloat(), 3.0f, 1.0f);
const int pitch = 3; const int pitch = 3;
const int glyphW = 5, glyphH = 7, glyphGap = 1; const int glyphW = 5, glyphH = 7, glyphGap = 1;
// Faint "off" LED grid // Faint "off" LED grid
g.setColour(juce::Colour(0xff0a200a)); g.setColour(juce::Colour(0xff1a1000));
for (int y = pitch / 2; y < b.getHeight(); y += pitch) for (int y = pitch / 2; y < b.getHeight(); y += pitch)
for (int x = pitch / 2; x < b.getWidth(); x += pitch) for (int x = pitch / 2; x < b.getWidth(); x += pitch)
g.fillRect(static_cast<float>(x) - 0.5f, static_cast<float>(y) - 0.5f, 1.0f, 1.0f); g.fillRect(static_cast<float>(x) - 0.5f, static_cast<float>(y) - 0.5f, 1.0f, 1.0f);
@ -402,9 +424,9 @@ void PatchLCD::paint(juce::Graphics& g) {
if (bits & (1 << (glyphW - 1 - col))) { if (bits & (1 << (glyphW - 1 - col))) {
float px = static_cast<float>(cx + col * pitch + pitch / 2); float px = static_cast<float>(cx + col * pitch + pitch / 2);
float py = static_cast<float>(cy + row * pitch + pitch / 2); float py = static_cast<float>(cy + row * pitch + pitch / 2);
g.setColour(juce::Colour(0xff1a330a)); g.setColour(juce::Colour(0xff3a2000));
g.fillEllipse(px - pitch * 0.5f, py - pitch * 0.5f, pitch, pitch); g.fillEllipse(px - pitch * 0.5f, py - pitch * 0.5f, pitch, pitch);
g.setColour(juce::Colour(0xff33ff66)); g.setColour(juce::Colour(0xffc59c07));
g.fillEllipse(px - pitch * 0.4f, py - pitch * 0.4f, pitch * 0.8f, pitch * 0.8f); g.fillEllipse(px - pitch * 0.4f, py - pitch * 0.4f, pitch * 0.8f, pitch * 0.8f);
} }
} }
@ -414,8 +436,32 @@ void PatchLCD::paint(juce::Graphics& g) {
} }
} }
// --- MIDI Signal LED ---
void MidiLed::paint(juce::Graphics& g) {
auto b = getLocalBounds().toFloat();
auto centre = b.getCentre();
float radius = juce::jmin(b.getWidth(), b.getHeight()) * 0.5f;
bool active = processor.isAnyNoteActive();
if (active) {
g.setColour(juce::Colour(0xffc59c07).withAlpha(0.2f));
g.fillEllipse(centre.x - radius * 1.8f, centre.y - radius * 1.8f, radius * 3.6f, radius * 3.6f);
}
g.setColour(juce::Colour(0xff1a1200));
g.fillEllipse(centre.x - radius, centre.y - radius, radius * 2.0f, radius * 2.0f);
if (active)
g.setColour(juce::Colour(0xffc59c07));
else
g.setColour(juce::Colour(0xff3a2a06));
g.fillEllipse(centre.x - radius * 0.75f, centre.y - radius * 0.75f, radius * 1.5f, radius * 1.5f);
}
MainContentComponent::MainContentComponent(ChromaFlockProcessor& p) MainContentComponent::MainContentComponent(ChromaFlockProcessor& p)
: processorRef(p), vuMeter(p), waveformDisplay(p), spectrumAnalyzer(p), pianoRoll(p) { : processorRef(p), vuMeter(p), waveformDisplay(p), spectrumAnalyzer(p), pianoRoll(p), midiLed(p) {
auto setupParam = [&](juce::Slider& knob, std::unique_ptr<SliderAttachment>& attach, auto setupParam = [&](juce::Slider& knob, std::unique_ptr<SliderAttachment>& attach,
const juce::String& paramId, const juce::String& name) { const juce::String& paramId, const juce::String& name) {
@ -534,6 +580,7 @@ MainContentComponent::MainContentComponent(ChromaFlockProcessor& p)
addAndMakeVisible(presetButton); addAndMakeVisible(presetButton);
addAndMakeVisible(patchLCD); addAndMakeVisible(patchLCD);
addAndMakeVisible(midiLed);
patchLCD.onClick = [this]() { showFileMenu(); }; patchLCD.onClick = [this]() { showFileMenu(); };
auto setupArrow = [&](juce::TextButton& btn) { auto setupArrow = [&](juce::TextButton& btn) {
@ -576,6 +623,12 @@ MainContentComponent::MainContentComponent(ChromaFlockProcessor& p)
scaleLabel.setJustificationType(juce::Justification::centredRight); scaleLabel.setJustificationType(juce::Justification::centredRight);
addAndMakeVisible(scaleLabel); addAndMakeVisible(scaleLabel);
midiLabel.setText("MIDI IN", juce::dontSendNotification);
midiLabel.setFont(juce::Font(9.0f));
midiLabel.setColour(juce::Label::textColourId, juce::Colour(0xff888888));
midiLabel.setJustificationType(juce::Justification::centredLeft);
addAndMakeVisible(midiLabel);
auto setupTransposeLabel = [&](juce::Label& label, const juce::String& text) { auto setupTransposeLabel = [&](juce::Label& label, const juce::String& text) {
label.setText(text, juce::dontSendNotification); label.setText(text, juce::dontSendNotification);
label.setFont(juce::Font(9.0f)); label.setFont(juce::Font(9.0f));
@ -916,6 +969,10 @@ void MainContentComponent::paint(juce::Graphics& g) {
auto drawSection = [&](int x, int y, int w, int h) { auto drawSection = [&](int x, int y, int w, int h) {
auto rect = juce::Rectangle<float>(static_cast<float>(x), static_cast<float>(y), auto rect = juce::Rectangle<float>(static_cast<float>(x), static_cast<float>(y),
static_cast<float>(w), static_cast<float>(h)); static_cast<float>(w), static_cast<float>(h));
// Soft glow behind section
auto glow = rect.expanded(6.0f);
g.setColour(juce::Colour(0xff222222).withAlpha(0.25f));
g.fillRoundedRectangle(glow, 12.0f);
// 3D fill gradient: slight light top, dark bottom // 3D fill gradient: slight light top, dark bottom
juce::ColourGradient fillGrad(juce::Colour(0xBB222222), rect.getCentreX(), rect.getY(), juce::ColourGradient fillGrad(juce::Colour(0xBB222222), rect.getCentreX(), rect.getY(),
juce::Colour(0xBB111111), rect.getCentreX(), rect.getBottom(), false); juce::Colour(0xBB111111), rect.getCentreX(), rect.getBottom(), false);
@ -938,6 +995,9 @@ void MainContentComponent::paint(juce::Graphics& g) {
auto drawSubSection = [&](int x, int y, int w, int h, const juce::String& title, int titlePadY = 2) { auto drawSubSection = [&](int x, int y, int w, int h, const juce::String& title, int titlePadY = 2) {
auto rect = juce::Rectangle<float>(static_cast<float>(x), static_cast<float>(y), auto rect = juce::Rectangle<float>(static_cast<float>(x), static_cast<float>(y),
static_cast<float>(w), static_cast<float>(h)); static_cast<float>(w), static_cast<float>(h));
auto glow = rect.expanded(6.0f);
g.setColour(juce::Colour(0xff222222).withAlpha(0.25f));
g.fillRoundedRectangle(glow, 12.0f);
juce::ColourGradient fillGrad(juce::Colour(0xBB222222), rect.getCentreX(), rect.getY(), juce::ColourGradient fillGrad(juce::Colour(0xBB222222), rect.getCentreX(), rect.getY(),
juce::Colour(0xBB111111), rect.getCentreX(), rect.getBottom(), false); juce::Colour(0xBB111111), rect.getCentreX(), rect.getBottom(), false);
g.setGradientFill(fillGrad); g.setGradientFill(fillGrad);
@ -950,8 +1010,8 @@ void MainContentComponent::paint(juce::Graphics& g) {
}; };
drawSubSection(10, 508, 510, 140, "DISTORTION", 6); drawSubSection(10, 508, 510, 140, "DISTORTION", 6);
drawSubSection(528, 508, 468, 140, "COMPRESSOR", 6); drawSubSection(528, 508, 468, 140, "COMPRESSION", 6);
drawSubSection(1004, 508, 268, 140, "AUTO-PAN", 6); drawSubSection(1004, 508, 268, 140, "PANNING", 6);
drawSubSection(1280, 508, 370, 140, "LIMITER", 6); drawSubSection(1280, 508, 370, 140, "LIMITER", 6);
// LFO sub-sections // LFO sub-sections
@ -1048,7 +1108,7 @@ void MainContentComponent::resized() {
distToneKnob.setBounds(dx + (fxKnobSize + 6) * 2, fxKnobY, fxKnobSize, fxKnobSize); distToneKnob.setBounds(dx + (fxKnobSize + 6) * 2, fxKnobY, fxKnobSize, fxKnobSize);
} }
// Compressor sub-section (X=528, W=470) // Compression sub-section (X=528, W=470)
{ {
int rowW = 5 * fxKnobSize + 4 * 6; int rowW = 5 * fxKnobSize + 4 * 6;
int sx = 528 + (470 - rowW) / 2; int sx = 528 + (470 - rowW) / 2;
@ -1142,6 +1202,9 @@ void MainContentComponent::resized() {
semitoneTransposeLabel.setBounds(1214, labelY2, 32, labelH2); semitoneTransposeLabel.setBounds(1214, labelY2, 32, labelH2);
semitoneTransposeBox.setBounds(1246, comboY2, 74, comboH2); semitoneTransposeBox.setBounds(1246, comboY2, 74, comboH2);
midiLabel.setBounds(1340, labelY2, 46, labelH2);
midiLed.setBounds(1390, (headerH - 10) / 2, 10, 10);
scaleLabel.setBounds(1514, labelY2, 42, labelH2); scaleLabel.setBounds(1514, labelY2, 42, labelH2);
uiScaleBox.setBounds(1560, comboY2, 80, comboH2); uiScaleBox.setBounds(1560, comboY2, 80, comboH2);
@ -1324,7 +1387,7 @@ void PianoRollComponent::paint(juce::Graphics& g) {
bool pressed = processor.isNoteActive(note); bool pressed = processor.isNoteActive(note);
if (pressed) { if (pressed) {
g.setColour(juce::Colour(0xffccaa44)); g.setColour(juce::Colour(0xff996600));
} else { } else {
juce::ColourGradient grad(juce::Colour(0xff444444), static_cast<float>(bx), 0.0f, juce::ColourGradient grad(juce::Colour(0xff444444), static_cast<float>(bx), 0.0f,
juce::Colour(0xff222222), static_cast<float>(bx), static_cast<float>(blackKeyHeight), false); juce::Colour(0xff222222), static_cast<float>(bx), static_cast<float>(blackKeyHeight), false);

View file

@ -67,6 +67,15 @@ private:
juce::String text = "INIT"; juce::String text = "INIT";
}; };
class MidiLed : public juce::Component, public juce::Timer {
public:
explicit MidiLed(ChromaFlockProcessor& p) : processor(p) { startTimerHz(30); }
void paint(juce::Graphics& g) override;
void timerCallback() override { repaint(); }
private:
ChromaFlockProcessor& processor;
};
class PianoRollComponent : public juce::Component, public juce::Timer { class PianoRollComponent : public juce::Component, public juce::Timer {
public: public:
explicit PianoRollComponent(ChromaFlockProcessor& p) : processor(p) { startTimerHz(60); } explicit PianoRollComponent(ChromaFlockProcessor& p) : processor(p) { startTimerHz(60); }
@ -129,6 +138,7 @@ private:
juce::Label osc1Label, osc2Label, filterLabel, envLabel, fEnvLabel, globalLabel; juce::Label osc1Label, osc2Label, filterLabel, envLabel, fEnvLabel, globalLabel;
juce::Label fxLabel, lfoLabel, fx2Label; juce::Label fxLabel, lfoLabel, fx2Label;
juce::Label scaleLabel; juce::Label scaleLabel;
juce::Label midiLabel;
juce::Label octaveTransposeLabel, semitoneTransposeLabel; juce::Label octaveTransposeLabel, semitoneTransposeLabel;
juce::ComboBox octaveTransposeBox, semitoneTransposeBox; juce::ComboBox octaveTransposeBox, semitoneTransposeBox;
@ -167,6 +177,7 @@ private:
// Preset menu // Preset menu
juce::TextButton presetButton{"PRESET"}; juce::TextButton presetButton{"PRESET"};
PatchLCD patchLCD; PatchLCD patchLCD;
MidiLed midiLed;
juce::TextButton prevPresetButton{"<"}, nextPresetButton{">"}; juce::TextButton prevPresetButton{"<"}, nextPresetButton{">"};
juce::TextButton randomizeButton{"RANDOM"}; juce::TextButton randomizeButton{"RANDOM"};
int currentPresetIndex = 0; int currentPresetIndex = 0;

View file

@ -276,7 +276,7 @@ void ChromaFlockProcessor::prepareToPlay(double sampleRate, int samplesPerBlock)
currentSampleRate = sampleRate; currentSampleRate = sampleRate;
synth.setCurrentPlaybackSampleRate(sampleRate); synth.setCurrentPlaybackSampleRate(sampleRate);
distortion.prepare(sampleRate); distortion.prepare(sampleRate);
compressor.prepare(sampleRate); compression.prepare(sampleRate);
limiter.prepare(sampleRate); limiter.prepare(sampleRate);
delay.prepare(sampleRate); delay.prepare(sampleRate);
reverbFX.prepare(sampleRate); reverbFX.prepare(sampleRate);
@ -407,7 +407,7 @@ void ChromaFlockProcessor::processBlock(juce::AudioBuffer<float>& buffer, juce::
getParam("distSymmetry"), getParam("distSymmetry"),
getParam("distTone")); getParam("distTone"));
compressor.setParameters( compression.setParameters(
getParam("compThreshold"), getParam("compThreshold"),
getParam("compRatio"), getParam("compRatio"),
getParam("compAttack"), getParam("compAttack"),
@ -449,8 +449,8 @@ void ChromaFlockProcessor::processBlock(juce::AudioBuffer<float>& buffer, juce::
right = distortion.process(right); right = distortion.process(right);
// Compression (stereo) // Compression (stereo)
left = compressor.process(left); left = compression.process(left);
right = compressor.process(right); right = compression.process(right);
// Brickwall limiter (stereo) // Brickwall limiter (stereo)
left = limiter.process(left); left = limiter.process(left);
@ -504,7 +504,7 @@ void ChromaFlockProcessor::processBlock(juce::AudioBuffer<float>& buffer, juce::
float rms = std::sqrt(sumSquares / static_cast<float>(numSamples)); float rms = std::sqrt(sumSquares / static_cast<float>(numSamples));
float level = juce::jmax(rms, peak); float level = juce::jmax(rms, peak);
level = std::min(level * 5.0f, 1.0f); level = std::min(level * 1.5f, 1.0f);
float prev = rmsLevel.load(std::memory_order_relaxed); float prev = rmsLevel.load(std::memory_order_relaxed);
if (level > prev) if (level > prev)

View file

@ -69,6 +69,11 @@ public:
return midiNote >= 0 && midiNote < 128 return midiNote >= 0 && midiNote < 128
&& activeNotes[midiNote].load(std::memory_order_relaxed); && activeNotes[midiNote].load(std::memory_order_relaxed);
} }
bool isAnyNoteActive() const {
for (auto& n : activeNotes)
if (n.load(std::memory_order_relaxed)) return true;
return false;
}
int getTransposeSemitones() const; int getTransposeSemitones() const;
@ -88,7 +93,7 @@ private:
static constexpr int maxVoices = 16; static constexpr int maxVoices = 16;
Distortion distortion; Distortion distortion;
Compressor compressor; Compression compression;
Limiter limiter; Limiter limiter;
Delay delay; Delay delay;
ReverbFX reverbFX; ReverbFX reverbFX;