64 g.fillAll(getLookAndFeel().findColour(juce::ResizableWindow::backgroundColourId));
67 auto width = getWidth();
68 auto height = getHeight();
70 auto channelMargin = outerMargin;
71 auto channelLabelWidth = 20;
74 if (numVisibleChannels == 0)
77 auto totalWidth = width - 2 * outerMargin - channelLabelWidth;
78 auto totalHeight = height - 2 * outerMargin - outerMargin;
81 auto totalChannelMargins = (numVisibleChannels - 1) * channelMargin;
82 auto availableHeightForPlots = totalHeight - totalChannelMargins;
83 auto channelPlotHeight = availableHeightForPlots / numVisibleChannels;
85 auto maxPlotFreq = 20000.0f;
86 auto minPlotFreq = 20.0f;
89 auto logScaleMin = log10(minPlotFreq);
90 auto logScaleMax = log10(maxPlotFreq);
91 auto logScaleRange = logScaleMax - logScaleMin;
93 auto holdColour = getLookAndFeel().findColour(JUCEAppBasics::CustomLookAndFeel::MeteringHoldColourId);
94 auto peakColour = getLookAndFeel().findColour(JUCEAppBasics::CustomLookAndFeel::MeteringPeakColourId);
95 auto markerColour = getLookAndFeel().findColour(juce::ResizableWindow::backgroundColourId);
96 auto legendColour = getLookAndFeel().findColour(juce::DrawableButton::textColourOnId);
99 for (
int channelIdx = 0; channelIdx < numVisibleChannels; ++channelIdx)
101 if (channelIdx >= m_plotPoints.size())
104 auto const& plotPoints = m_plotPoints[channelIdx];
107 auto visuAreaY = outerMargin + channelIdx * (channelPlotHeight + channelMargin);
108 juce::Rectangle<int> visuArea(outerMargin, visuAreaY, totalWidth, channelPlotHeight);
110 auto visuAreaOrigX = float(outerMargin);
111 auto visuAreaOrigY = float(visuAreaY + channelPlotHeight);
112 auto visuAreaWidth = totalWidth;
113 auto visuAreaHeight = channelPlotHeight;
116 g.setColour(getLookAndFeel().findColour(juce::Slider::backgroundColourId));
117 g.fillRect(visuArea);
120 juce::Rectangle<int> channelLabelArea(outerMargin + totalWidth, visuAreaY, channelLabelWidth, channelPlotHeight);
121 g.setColour(getLookAndFeel().findColour(juce::ResizableWindow::backgroundColourId));
122 g.fillRect(channelLabelArea);
126 g.setColour(legendColour);
127 g.drawText(juce::String(channelIdx + 1),
128 channelLabelArea.toFloat(),
129 juce::Justification::centred,
true);
132 auto markerLineValues = std::vector<float>{ 20.f, 30.f, 40.f, 50.f, 60.f, 70.f, 80.f, 90.f, 100.f, 200.f, 300.f, 400.f, 500.f, 600.f, 700.f, 800.f, 900.f, 1000.f, 2000.f, 3000.f, 4000.f, 5000.f, 6000.f, 7000.f, 8000.f, 9000.f, 10000.f, 20000.f };
134 for (
auto i = 0; i < markerLineValues.size(); ++i)
136 auto skewedProportionX = (log10(markerLineValues.at(i)) - logScaleMin) / logScaleRange;
137 auto posX = visuAreaOrigX + (
static_cast<float>(visuAreaWidth) * skewedProportionX);
138 g.setColour(markerColour);
139 g.drawLine(juce::Line<float>(posX, visuAreaOrigY, posX, visuAreaOrigY - visuAreaHeight));
144 g.setColour(getLookAndFeel().findColour(juce::LookAndFeel_V4::ColourScheme::menuText));
146 juce::Rectangle<float>(visuAreaOrigX + visuAreaWidth - 120.0f,
float(visuAreaY), 110.0f,
float(outerMargin)),
147 juce::Justification::centred,
true);
150 if (!plotPoints.peaks.empty() && plotPoints.holds.size() == plotPoints.peaks.size())
153 auto getBandFrequency = [&plotPoints](
int bandIndex) ->
float
155 if (plotPoints.minFreq <= 0.0f || plotPoints.maxFreq <= plotPoints.minFreq)
158 float ratio = plotPoints.maxFreq / plotPoints.minFreq;
159 float t =
static_cast<float>(bandIndex) / (plotPoints.peaks.size() - 1);
160 return plotPoints.minFreq * std::pow(ratio, t);
164 auto frequencyToScreenX = [&](
float frequency) ->
float
166 if (frequency <= 0.0f)
167 return visuAreaOrigX;
169 float skewedProportionX = (log10(frequency) - logScaleMin) / logScaleRange;
170 return visuAreaOrigX + (
static_cast<float>(visuAreaWidth) * skewedProportionX);
177 for (
int i = 0; i < plotPoints.peaks.size(); ++i)
179 float bandFreq = getBandFrequency(i);
181 if (minPlotIdx == -1 && bandFreq >= minPlotFreq)
184 if (bandFreq <= maxPlotFreq)
189 if (minPlotIdx == -1)
191 if (maxPlotIdx == -1 || maxPlotIdx >= plotPoints.peaks.size())
192 maxPlotIdx =
static_cast<int>(plotPoints.peaks.size() - 1);
194 if (minPlotIdx <= maxPlotIdx)
197 auto holdPath = juce::Path{};
198 auto peakPath = juce::Path{};
201 float startFreq = getBandFrequency(minPlotIdx);
202 float startX = frequencyToScreenX(startFreq);
203 float startHoldY = visuAreaOrigY - plotPoints.holds.at(minPlotIdx) * visuAreaHeight;
204 float startPeakY = visuAreaOrigY - plotPoints.peaks.at(minPlotIdx) * visuAreaHeight;
206 holdPath.startNewSubPath(juce::Point<float>(startX, startHoldY));
207 peakPath.startNewSubPath(juce::Point<float>(startX, startPeakY));
210 for (
int i = minPlotIdx + 1; i <= maxPlotIdx; ++i)
212 float bandFreq = getBandFrequency(i);
213 float pointX = frequencyToScreenX(bandFreq);
215 float holdY = visuAreaOrigY - plotPoints.holds.at(i) * visuAreaHeight;
216 holdPath.lineTo(juce::Point<float>(pointX, holdY));
218 float peakY = visuAreaOrigY - plotPoints.peaks.at(i) * visuAreaHeight;
219 peakPath.lineTo(juce::Point<float>(pointX, peakY));
222 g.setColour(holdColour);
223 g.strokePath(holdPath, juce::PathStrokeType(1));
224 g.setColour(peakColour);
225 g.strokePath(peakPath, juce::PathStrokeType(3));
231 auto markerLegendValues = std::map<float, std::string>{ {20.f,
"20"}, {100.f,
"100"}, {1000.f,
"1k"}, {10000.f,
"10k"}, {20000.f,
"20k"} };
232 auto legendValueWidth = 40.0f;
233 auto legendY = height - outerMargin - outerMargin;
235 for (
auto const& [freq, label] : markerLegendValues)
237 auto skewedProportionX = (log10(freq) - logScaleMin) / logScaleRange;
238 auto posX = float(outerMargin) + (
static_cast<float>(totalWidth) * skewedProportionX);
240 g.setColour(legendColour);
242 juce::Rectangle<float>(posX - 0.5f * legendValueWidth,
float(legendY), legendValueWidth, float(outerMargin)),
243 juce::Justification::centred,
true);