feat(gui): add GUI components and displays

This commit is contained in:
2026-09-08 14:55:22 +02:00
parent 5a80534a2e
commit a9abca9e15
19 changed files with 898 additions and 0 deletions
+79
View File
@@ -0,0 +1,79 @@
#include "FilterDisplay.h"
namespace serum
{
float FilterDisplay::magnitude (float freqHz, float cutoffHz, float res, int type)
{
const float w = (cutoffHz > 1.0f) ? freqHz / cutoffHz : 1.0f;
const float q = 0.6f + res * 14.0f;
const float w2 = w * w;
const float denom = std::sqrt ((1.0f - w2) * (1.0f - w2) + (w / q) * (w / q));
switch ((FilterModel) type)
{
case FilterModel::LadderHP: return (denom > 1e-6f) ? w2 / denom : 0.0f;
case FilterModel::LadderBP: return (denom > 1e-6f) ? (w / q) / denom : 0.0f;
case FilterModel::Screamer: return (denom > 1e-6f) ? (w / (q * 0.7f)) / denom : 0.0f;
case FilterModel::Comb:
return 0.5f + 0.5f * std::cos (2.0f * juce::MathConstants<float>::pi * freqHz / cutoffHz);
case FilterModel::Formant:
{
float m = 0.0f;
const float fc[3] = { cutoffHz * 0.5f, cutoffHz * 1.6f, cutoffHz * 3.2f };
for (int i = 0; i < 3; ++i)
{
const float ww = freqHz / fc[i];
const float dd = std::sqrt ((1.0f - ww * ww) * (1.0f - ww * ww) + (ww / q) * (ww / q));
m += (ww / q) / (dd + 1e-6f);
}
return m / 3.0f;
}
case FilterModel::Diode:
default:
return (denom > 1e-6f) ? 1.0f / denom : 0.0f;
}
}
void FilterDisplay::setParams (int t, float c, float r, float d, int s)
{
type = t; cutoff = c; res = r; drive = d; slope = s;
}
void FilterDisplay::paint (juce::Graphics& g)
{
const auto b = getLocalBounds().toFloat();
g.setColour (juce::Colours::black.withAlpha (0.4f));
g.fillRoundedRectangle (b, 4.0f);
g.setColour (theme::outline);
g.drawRoundedRectangle (b.reduced (0.5f), 4.0f, 1.0f);
if (! enabled)
return;
const float cutoffHz = maps::cutoffToHz (cutoff);
const int n = 300;
juce::Path path;
path.startNewSubPath (b.getX() + 4.0f, b.getBottom() - 4.0f);
for (int i = 0; i <= n; ++i)
{
const float logF = std::log10 (20.0f) + ((float) i / (float) n) * (std::log10 (20000.0f) - std::log10 (20.0f));
const float freq = std::pow (10.0f, logF);
const float mag = magnitude (freq, cutoffHz, res, type);
const float db = juce::Decibels::gainToDecibels (mag + 1e-5f);
const float x = b.getX() + 4.0f + ((float) i / (float) n) * (b.getWidth() - 8.0f);
const float y = juce::jmap (db, -30.0f, 12.0f, b.getBottom() - 4.0f, b.getY() + 4.0f);
path.lineTo (x, y);
}
path.lineTo (b.getRight() - 4.0f, b.getBottom() - 4.0f);
path.closeSubPath();
g.setColour (theme::accent.withAlpha (0.25f));
g.fillPath (path);
g.setColour (theme::accent);
g.strokePath (path, juce::PathStrokeType (1.5f));
}
} // namespace serum