Skip to content

Commit 79f15e7

Browse files
committed
LevelsProcessor fixes and Meter work.
1 parent f993638 commit 79f15e7

File tree

4 files changed

+83
-79
lines changed

4 files changed

+83
-79
lines changed

modules/squarepine_audio/effects/LevelsProcessor.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,9 @@ void LevelsProcessor::processBlock (juce::AudioBuffer<double>& b, MidiBuffer&)
7373
template<typename FloatType>
7474
void LevelsProcessor::process (juce::AudioBuffer<FloatType>& buffer)
7575
{
76+
volatile ScopedNoDenormals noDenormals;
77+
ignoreUnused (noDenormals);
78+
7679
const auto mode = getMeteringMode();
7780
const auto b = isBypassed();
7881

modules/squarepine_audio/effects/LevelsProcessor.h

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ class LevelsProcessor final : public InternalProcessor
5959
for (auto& a : { &channels, &tempBuffer })
6060
{
6161
a->resize (numChannels);
62-
a->clearQuick();
62+
a->fill (static_cast<FloatType> (0));
6363
}
6464
}
6565

@@ -68,7 +68,7 @@ class LevelsProcessor final : public InternalProcessor
6868
MeteringMode meteringMode,
6969
bool possiblyBypassed)
7070
{
71-
tempBuffer.clearQuick();
71+
tempBuffer.fill (static_cast<FloatType> (0));
7272

7373
if (! possiblyBypassed)
7474
{
@@ -78,19 +78,19 @@ class LevelsProcessor final : public InternalProcessor
7878
{
7979
case MeteringMode::peak:
8080
for (int i = 0; i < numChannels; ++i)
81-
tempBuffer.add ((FloatType) buffer.getMagnitude (i, 0, buffer.getNumSamples()));
81+
tempBuffer.set (i, (FloatType) buffer.getMagnitude (i, 0, buffer.getNumSamples()));
8282
break;
8383

8484
case MeteringMode::rms:
8585
for (int i = 0; i < numChannels; ++i)
86-
tempBuffer.add ((FloatType) buffer.getRMSLevel (i, 0, buffer.getNumSamples()));
86+
tempBuffer.set (i, (FloatType) buffer.getRMSLevel (i, 0, buffer.getNumSamples()));
8787
break;
8888

8989
case MeteringMode::midSide:
9090
for (int i = 0; i < numChannels; ++i)
9191
{
9292
const auto v = buffer.getMagnitude (i, 0, buffer.getNumSamples());
93-
tempBuffer.add ((FloatType) square (v));
93+
tempBuffer.set (i, (FloatType) square (v));
9494
}
9595
break;
9696

modules/squarepine_audio/graphics/Meter.cpp

Lines changed: 64 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -87,38 +87,60 @@ void Meter::checkModelPtrIsValid() const
8787
void Meter::resized()
8888
{
8989
gradient = {};
90+
imageSource = {};
91+
imageClipped = {};
9092

9193
if (model == nullptr)
9294
return;
9395

9496
auto positions = model->getColourPositions();
95-
if (positions.size() < 2)
96-
{
97-
jassertfalse;
98-
return;
99-
}
97+
98+
// Must have at least 1 colour!
99+
jassert (! positions.empty());
100+
101+
const auto b = getLocalBounds();
100102

101103
std::sort (std::begin (positions), std::end (positions),
102104
[] (const auto& lhs, const auto& rhs) { return lhs.decibels < rhs.decibels; });
103105

104-
const auto b = getLocalBounds();
105106
const bool isHorizontal = model->isHorizontal();
106107
if (isHorizontal)
107108
gradient = ColourGradient::horizontal (positions.front().colour, positions.back().colour, b);
108109
else
109110
gradient = ColourGradient::vertical (positions.front().colour, positions.back().colour, b);
110111

111-
for (size_t i = 1; i < (positions.size() - 1); ++i)
112-
positions[i].addToGradient (gradient, isHorizontal);
112+
if (const auto numPos = (int64) positions.size(); numPos > 1)
113+
for (int64 i = 1; i < (numPos - 1); ++i)
114+
positions[(size_t) i].addToGradient (gradient, isHorizontal);
115+
116+
{
117+
imageSource = { Image::RGB, getWidth(), getHeight(), false };
118+
Graphics ig (imageSource);
119+
ig.setGradientFill (gradient);
120+
ig.fillAll();
121+
}
122+
123+
imageClipped = imageSource;
124+
// TODO force refresh async + repaint
113125
}
114126

115127
void Meter::paint (Graphics& g)
116128
{
117-
if (gradient.getNumColours() <= 0)
129+
//auto b = getLocalBounds();
130+
//DBG (b.toString());
131+
132+
if (imageSource.isNull())
118133
return; // Nothing to draw.
119134

120-
g.setGradientFill (gradient);
121-
g.fillAll();
135+
g.setColour (Colours::red);
136+
137+
for (const auto& c : channels)
138+
{
139+
// imageClipped = imageSource.getClippedImage (c.meterArea);
140+
// g.drawImage (imageClipped, c.meterArea.toFloat(), RectanglePlacement::doNotResize);
141+
142+
g.fillRect (c.meterArea);
143+
}
122144
}
123145

124146
bool Meter::refresh()
@@ -135,37 +157,51 @@ bool Meter::refresh()
135157
needsMaxLevel = model->needsMaxLevel();
136158
}
137159

138-
bool areLevelsDifferent = false;
139-
bool isMaxLevelDelayExpired = false;
140160
const auto numChans = std::min (levels.size(), channels.size());
161+
const auto chanWidthPx = [&]()
162+
{
163+
auto v = roundToIntAccurate ((double) getWidth() / (double) numChans);
164+
v -= 2;
165+
jassert (v > 2);
166+
return v;
167+
}();
168+
169+
const auto hPx = getHeight();
170+
171+
bool areLevelsDifferent = channels.getFirst().meterArea.getWidth() != chanWidthPx;
172+
bool isMaxLevelDelayExpired = false;
141173

142174
for (int i = 0; i < numChans; ++i)
143175
{
144176
auto& channel = channels.getReference (i);
145-
auto level = lerp (channel.getLevel(), levels[i], 0.9f);
146-
dsp::util::snapToZero (level);
147-
channel.setLevel (level);
177+
const auto lastLevel = channel.level;
178+
const auto lastMaxLevel = channel.maxLevel;
179+
180+
channel.level = lerp (lastLevel, levels[i], 0.9f);
148181

149182
if (needsMaxLevel)
150183
{
151-
if (channel.getLevel() > channel.getMaxLevel())
184+
if (channel.level > lastMaxLevel)
152185
{
153-
channel.setLastMaxAudioLevelTime (Time::currentTimeMillis());
154-
channel.setMaxLevel (channel.getLevel());
186+
channel.timeOfMaximumMs = Time::currentTimeMillis();
187+
channel.maxLevel = lastLevel;
155188
}
156-
else if (Time::currentTimeMillis() - channel.getLastMaxAudioLevelTime() > maxLevelExpiryMs)
189+
else if (Time::currentTimeMillis() - channel.timeOfMaximumMs > maxLevelExpiryMs)
157190
{
158-
channel.setMaxLevel (channel.getMaxLevel() * decayRate);
191+
channel.maxLevel = lastMaxLevel * decayRate;
159192
isMaxLevelDelayExpired = true;
160193
}
161194

162-
areLevelsDifferent |= ! approximatelyEqual (channel.getMaxLevel(), channel.getLastMaxLevel());
195+
areLevelsDifferent |= ! approximatelyEqual (channel.maxLevel, lastMaxLevel);
163196
}
164197

165-
areLevelsDifferent |= ! approximatelyEqual (channel.getLevel(), channel.getLastLevel());
198+
areLevelsDifferent |= ! approximatelyEqual (channel.level, lastLevel);
199+
200+
const auto h = (double) hPx * (double) DecibelHelpers::gainToMeterProportion ((double) channel.level);
166201

167-
channel.setLastLevel (channel.getLevel());
168-
channel.setLastMaxLevel (channel.getMaxLevel());
202+
channel.meterArea = channel.meterArea
203+
.withWidth (chanWidthPx)
204+
.withHeight (roundToIntAccurate (h));
169205
}
170206

171207
if (areLevelsDifferent)
@@ -174,11 +210,11 @@ bool Meter::refresh()
174210
return areLevelsDifferent;
175211
}
176212

177-
void Meter::updateClippingLevel (bool timeToUpdate)
213+
void Meter::updateClippingLevel (bool forceUpdate)
178214
{
179215
auto maxLevel = 0.0f;
180216
for (const auto& channel : channels)
181-
maxLevel = std::max (channel.getLevel(), maxLevel);
217+
maxLevel = std::max (channel.level, maxLevel);
182218

183219
maxLevel = Decibels::gainToDecibels (maxLevel);
184220

@@ -190,6 +226,6 @@ void Meter::updateClippingLevel (bool timeToUpdate)
190226
currClippingLevel = ClippingLevel::warning;
191227

192228
// Only update if higher, or if the time delay has expired.
193-
if (clippingLevel < currClippingLevel || timeToUpdate)
229+
if (clippingLevel < currClippingLevel || forceUpdate)
194230
clippingLevel = currClippingLevel;
195231
}

modules/squarepine_audio/graphics/Meter.h

Lines changed: 11 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,8 @@ struct DecibelHelpers final
33
{
44
enum
55
{
6-
// The maximum gain value of the slider in decibels.
7-
maxSliderLevelDb = 12,
8-
9-
// The minimum decibel level of the slider.
10-
minSliderLevelDb = -100,
6+
maxSliderLevelDb = 12, // The maximum gain value of the slider in decibels.
7+
minSliderLevelDb = -100, // The minimum decibel level of the slider.
118

129
// The magnitude of spatial compression of lower decibel values versus higher values.
1310
// Ranges from 0 (linear) to infinity (hard knee).
@@ -41,7 +38,7 @@ struct DecibelHelpers final
4138
int maximumDecibels = maxSliderLevelDb) noexcept;
4239

4340
/** @returns the proportion of the meter length for the decibel value.
44-
*
41+
4542
@param meterProportion The proportion of the meter length.
4643
@param minimumDecibels The minimum level of the meter in decibels.
4744
@param maximumDecibels The maximum level of the meter in decibels.
@@ -51,7 +48,7 @@ struct DecibelHelpers final
5148
int maximumDecibels = maxSliderLevelDb) noexcept;
5249

5350
/** @returns the proportion of the meter height for the decibel value.
54-
*
51+
5552
@param decibels The volume value in decibels.
5653
@param minimumDecibels The minimum level of the meter in decibels
5754
@param maximumDecibels The maximum level of the meter in decibels
@@ -213,53 +210,19 @@ class Meter final : public Component
213210

214211
//==============================================================================
215212
/** */
216-
struct ChannelContext
213+
struct ChannelContext final
217214
{
218-
ChannelContext() = default;
219-
ChannelContext (const ChannelContext&) = default;
220-
ChannelContext (ChannelContext&&) = default;
221-
~ChannelContext() = default;
222-
ChannelContext& operator= (const ChannelContext&) = default;
223-
ChannelContext& operator= (ChannelContext&&) = default;
224-
225-
void setLevel (float newLevel) { set (level, newLevel); }
226-
float getLevel() const noexcept { return level; }
227-
228-
void setLastLevel (float newLastLevel) { set (lastLevel, newLastLevel); }
229-
float getLastLevel() const noexcept { return lastLevel; }
230-
231-
void setMaxLevel (float newMaxLevel) { set (maxLevel, newMaxLevel); }
232-
float getMaxLevel() const noexcept { return maxLevel; }
233-
234-
void setLastMaxLevel (float newLastMaxLevel) { set (lastMaxLevel, newLastMaxLevel); }
235-
float getLastMaxLevel() const noexcept { return lastMaxLevel; }
236-
237-
void setLastMaxAudioLevelTime (int64 t) { timeOfMaximumMs = t; }
238-
int64 getLastMaxAudioLevelTime() const noexcept { return timeOfMaximumMs; }
239-
240-
void setMeterArea (const Rectangle<int>& area) { meterArea = area; }
241-
const Rectangle<int>& getMeterArea() const noexcept { return meterArea; }
242-
243-
private:
244215
float level = 0.0f, // The last measured audio absolute volume level.
245-
lastLevel = 0.0f, // The volume level of the last update, used to check if levels have changed for repainting.
246-
maxLevel = 0.0f, // The maximum audio levels of the trailing 3 seconds.
247-
lastMaxLevel = 0.0f; // The max volume level of the last update.
216+
maxLevel = 0.0f; // The maximum audio levels of the trailing level (by default this is 3 seconds).
248217
int64 timeOfMaximumMs = 0; // The time of the last maximum audio level.
249-
Rectangle<int> meterArea; // The left/right drawable regions for the meter.
250-
251-
void set (float& value, float newValue)
252-
{
253-
dsp::util::snapToZero (newValue);
254-
value = newValue;
255-
}
218+
Rectangle<int> meterArea; // The drawable regions for the meter's channels (eg: left, right).
256219
};
257220

258221
/** */
259222
const ChannelContext& getChannel (int channel) const noexcept { return channels.getReference (channel); }
260223

261224
/** */
262-
float getChannelLevel (int channel) const noexcept { return getChannel (channel).getLevel(); }
225+
float getChannelLevel (int channel) const noexcept { return getChannel (channel).level; }
263226

264227
//==============================================================================
265228
/** */
@@ -288,7 +251,9 @@ class Meter final : public Component
288251
Array<ChannelContext> channels;
289252
Array<float> levels;
290253
ClippingLevel clippingLevel = ClippingLevel::none;
254+
291255
ColourGradient gradient;
256+
Image imageSource, imageClipped;
292257

293258
void assignModelPtr (MeterModel*);
294259

@@ -298,7 +263,7 @@ class Meter final : public Component
298263
#endif
299264

300265
//==============================================================================
301-
void updateClippingLevel (bool timeToUpdate);
266+
void updateClippingLevel (bool forceUpdate);
302267

303268
//==============================================================================
304269
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Meter)

0 commit comments

Comments
 (0)