Files
serumalt/Source/GUI/EnvelopeDisplay.cpp
T
biggy e4cea300db chore: land janitor round 1 and graft integration
Round 1 findings and fixes, all previously uncommitted:
- FXProcessor per-slot DSP history repair (HEAD does not compile without it)
- SynthVoice::getModulationValue, editor control-lock guard
- ExactCompare.h naming the deliberate exact float comparisons
- KnobLookAndFeel replacing the never-installed SerumLookAndFeel
- 55 first-party compiler warnings to 0
- AGENT.md renamed to AGENTS.md across Source/
- graft repository context graph and its hooks
- JANITOR_TASKS.md and UI_UX_AUDIT.md records
2026-10-06 08:50:45 +02:00

86 lines
2.7 KiB
C++

#include "EnvelopeDisplay.h"
#include "../Envelope.h"
#include "../ExactCompare.h"
namespace serum
{
void EnvelopeDisplay::setParams (float a, float d, float s, float r, float c)
{
if (! changed (attack, a) && ! changed (decay, d) && ! changed (sustain, s)
&& ! changed (release, r) && ! changed (curve, c))
return;
attack = a; decay = d; sustain = s; release = r; curve = c;
repaint();
}
void EnvelopeDisplay::paint (juce::Graphics& g)
{
const auto b = getLocalBounds().toFloat().reduced (4.0f);
g.setColour (juce::Colours::black.withAlpha (0.4f));
g.fillRoundedRectangle (b.expanded (4.0f), 4.0f);
g.setColour (theme::outline);
g.drawRoundedRectangle (b.expanded (4.0f).reduced (0.5f), 4.0f, 1.0f);
const float atkShape = attackShapeFor (curve);
const float decShape = decayShapeFor (curve);
// Normalise durations for display, allowing a short sustain plateau.
const float aSec = maps::toSeconds (attack);
const float dSec = maps::toSeconds (decay);
const float rSec = maps::toSeconds (release);
const float total = aSec + dSec + rSec + 0.01f;
const float ax = (aSec / total);
const float dx = (dSec / total);
const float rx = (rSec / total);
const float left = b.getX();
const float right = b.getRight();
const float bottom = b.getBottom();
const float top = b.getY();
const float sustainY = bottom - sustain * b.getHeight();
juce::Path path;
path.startNewSubPath (left, bottom);
// Attack (curve-shaped).
const int steps = 48;
const float peakX = left + ax * b.getWidth();
for (int i = 0; i <= steps; ++i)
{
const float p = (float) i / steps;
const float y = bottom - (bottom - top) * std::pow (p, atkShape);
path.lineTo (left + p * (peakX - left), y);
}
// Decay to sustain.
const float decX = peakX + dx * b.getWidth();
for (int i = 0; i <= steps; ++i)
{
const float p = (float) i / steps;
const float y = sustainY + (top - sustainY) * std::pow (1.0f - p, decShape);
path.lineTo (peakX + p * (decX - peakX), y);
}
path.lineTo (decX, sustainY);
path.lineTo (right - rx * b.getWidth(), sustainY);
// Release.
const float relStartX = right - rx * b.getWidth();
for (int i = 0; i <= steps; ++i)
{
const float p = (float) i / steps;
const float y = bottom - (bottom - sustainY) * std::pow (1.0f - p, decShape);
path.lineTo (relStartX + p * (right - relStartX), y);
}
g.setColour (theme::accent2.withAlpha (0.3f));
juce::Path fill = path;
fill.lineTo (right, bottom);
fill.closeSubPath();
g.fillPath (fill);
g.setColour (theme::accent2);
g.strokePath (path, juce::PathStrokeType (1.5f));
}
} // namespace serum