#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::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) { if (type == t && cutoff == c && res == r && drive == d && slope == s) return; type = t; cutoff = c; res = r; drive = d; slope = s; repaint(); } 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