- 36 files move into the module whose AGENTS.md already claimed them: Engine/, Synth/, Modulation/, Params/, Plugin/, GUI/, EffectUnits/, Resources/ - Source/engine, modulation, params, plugin and synth become Engine, Modulation, Params, Plugin and Synth, matching the directories that already hold code - ExactCompare.h stays at the Source/ root as the one shared header - SERUMALT_INCLUDE_DIRS adds one include directory per module, so the 31 parent-relative includes become plain names with no path prefix - the module AGENTS.md files list owned files instead of describing a move that had not happened, and README.md and AGENTS.md describe the real tree Verified: the harness passes CTest, the Linux VST3 and Standalone link, the Windows cross-build passes its module checks, each configure exposes only its own targets, no first-party warning line appears, and the generated Windows manifest is unchanged.
86 lines
2.7 KiB
C++
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
|