"IVDisplayControl" best starting point for spectrum display or?

Sure

Class:

class Plotter : protected virtual GraphicsTypes
{
public:
    
    struct Style
    {
        Style() : mFill(false), mThickness(1.f) {}
        
        Style(const IColor color, bool fill, float thickness = 1.f)
        : mColor(color), mFill(fill), mThickness(thickness) {}
        
        IColor mColor;
        bool mFill = false;
        float mThickness = 1.f;
    };
    
    Plotter(const IRECT& bounds) : mBounds(bounds) {}
    
    void SetBounds(const IRECT& bounds) { mBounds = bounds; }
    
    void Plot(IGraphics& g, const float* normY, unsigned long N, const Style& style);
    void Plot(IGraphics& g, const float* normX, const float* normY, unsigned long N, const Style& style);
    
protected:
    
    float XCalc(float xNorm) { return mBounds.L + (mBounds.W() * xNorm); }
    float YCalc(float yNorm) { return mBounds.B - (mBounds.H() * yNorm); }
    
    void Complete(IGraphics& g, const Style& s);
    
    IRECT mBounds;
}

Implementation::


void Plotter::Plot(IGraphics& g, const float* normY, unsigned long N, const Style& style)
{
    g.PathClear();
    g.PathClipRegion(mBounds);
    
    g.PathMoveTo(mBounds.L, YCalc(normY[0]));
    
    for (unsigned long i = 1; i < N; i++)
        g.PathLineTo(XCalc((1.f / (N - 1)) * i), YCalc(normY[i]));
    
    Complete(g, style);
}
    
void Plotter::Plot(IGraphics& g, const float* normX, const float* normY, unsigned long N, const Style& style)
{
    // The x values must be in order, so we can trim the ends
    
    for (; N > 1 && normX[0] < 0.f && normX[1] < 0.f; N--)
    {
        normX++;
        normY++;
    }
    
    for (; N > 1 && normX[N - 2] > 1.f && normX[N - 1] > 1.f; N--);
    
    if (!N)
        return;
    
    g.PathClear();
    g.PathClipRegion(mBounds);
    
    // Draw
    
    if (normX[0] >= 0.f)
    {
        g.PathMoveTo(mBounds.L, YCalc(normY[0]));
    }
    else
    {
        g.PathMoveTo(normX[0], YCalc(normY[0]));
        normX++;
        normY++;
        N--;
    }
   
    for (unsigned long i = 0 ; i < N; i++)
        g.PathLineTo(XCalc(normX[i]), YCalc(normY[i]));
    
    if (N && normX[N - 1] < 1.f)
        g.PathLineTo(mBounds.R,  YCalc(normY[N - 1]));
    
    Complete(g, style);
}

void Plotter::Complete(IGraphics& g, const Style& s)
{
    if (s.mFill)
    {
        g.PathLineTo(mBounds.R, mBounds.B);
        g.PathLineTo(mBounds.L, mBounds.B);
        g.PathClose();
        g.PathFill(s.mColor);
    }
    else
    {
        IStrokeOptions options;
        options.mJoinOption = ELineJoin::Round;
        g.PathStroke(s.mColor, s.mThickness, options);
    }
    
    g.PathClipRegion();
}

GraphicsType is a convenience class that pulls a bunch of iplug/igraphics types into a local scope, so they don’t require explicit namespace access.

Thank you! Greatly appreciated :slightly_smiling_face:

If you get a chance I would greatly appreciate some insight on how you did this. Do you pass the computed FFT array(s) back and forth between UI thread and Audio thread or some other means?

I use a blocking DSP algorithm on the DSP side of things to collect samples - this allows me to collect blocks of size N at a hop (interval in samples) of K. Each time I have gathered a full N samples I copy them to elements of an IPlugQueue<> and then that queue is checked in OnIdle() (this is the sender pattern, as used in other igraphics controls). Mine is hand rolled, but I’d suggest checking out ISenderData (which I think didn’t exist when I was doing this, or I hadn’t noticed it).

In my case everything else happens from OnIdle() - FFT and smoothing etc. This ensures that I minimise processing when the UI is closed.

1 Like

WIP:

1 Like

Awesome! Will be a great addition to iPlug2. I will try to contribute to development and test here the best I can. Hope some of my posts above offered something to the process.

Thank you!

Integrated @AlexHarker 's Spectrum Plotter code. Currently looking a bit like this:

3 Likes