Level meter bar with two colours

Hello JUCE community

im creating a level meter and i want the portion of the bar above the 0 dB point to turn red. I’ve been trying to get this to work but it’s not doing exactly what I want, I was wondering how other people do this. Here is my level meter class :

class meter : public Component
{
public:
meter() : maxTruePeak(-60.0f)
{
}

void paint(Graphics& g) override
{
auto bounds = getLocalBounds().toFloat();

// Fill the entire rectangle with the background color
g.setColour(Colour(uint8(67), 47, 49, 100.f));
g.fillRoundedRectangle(bounds, 1.f);

// Check if maxTruePeak is above 0 dB
if (maxTruePeak > 0.0f)
{
    // Calculate the height of the red portion based on maxTruePeak
    const auto redHeight = jmap(maxTruePeak, 0.f, 6.f, 0.f, static_cast<float>(getHeight()));
    
    // Set the color to red for the portion above 0 dB
    g.setColour(Colours::red);
    
    // Fill the rectangle with red color for the portion above 0 dB
    g.fillRoundedRectangle(bounds.removeFromTop(redHeight), 1.f);
}

// Set the color for the portion below 0 dB
g.setColour(Colour(uint8(235), 80, 42, 1.f));

// Calculate the height of the non-red portion based on maxTruePeak
const auto scaledY = jmap(maxTruePeak, -60.f, 0.f, 0.f, static_cast<float>(getHeight()));

// Fill the rectangle with color for the non-red portion
g.fillRoundedRectangle(bounds.removeFromBottom(scaledY), 1.f);

}

private:
float maxTruePeak;
};

You should look at doing this with a ColourGradient and gradientFill(), and then you can just fill the whole thing based on the value (instead of doing the two halves separately), and when it goes over the threshold the gradient changes colour.

You can do this with a ColourGradient, but if you want flat colours anyway, I would not use a ColourGradient, because it will be more expensive to write. Each pixel needs a lookup.

To find the position use jmap:

// assumed setup
float silence = -60.0f;
float max = 12.0f;
float level = -20.0f; // the level in dB
float level = juce::Decibels::gainToDecibels (0.7, silence);

juce::Rectangle<float> bounds; // set this up according to your layout

// helper for conversion
auto levelToY = [&](auto level)
{
    return jmap (level, silence, max, bounds.getBottom(), bounds.getY());
};

// draw lower half first
auto lower = bounds.withTop (levelToY (std::min (level, 0.0f));
g.setColour (juce::Colours::green);
g.fillRect (lower.toNearestInt());

// draw upper if level > 0.0
if (level > 0.0f)
{
    auto upper = bounds.withBottom (lower.getY()).withY (levelToY (level));
    g.setColour (juce::Colours::red);
    g.fillRect (upper.toNearestInt());
}

Untested code, good luck