167 static constexpr double min_dB = -60.0;
168 static constexpr double preferred_strip_width = 16.0;
169 static constexpr double min_strip_spacing = 1.0;
170 static constexpr double preferred_strip_spacing = 3.0;
171 static constexpr double label_width = 30.0;
172 static constexpr double top_margin = 4.0;
173 static constexpr double bottom_margin = 12.0;
175 static constexpr double min_width_for_labels = 8.0;
182 float peak_hold = 0.f;
184 std::vector<ChannelDisplay> m_channels;
188 QGraphicsItem* parent)
189 :
Process::EffectLayerView{parent}
191 setAcceptedMouseButtons({});
196 auto* audio_inlet = process.inlets().front();
197 auto fact = portFactory.get(audio_inlet->concreteKey());
198 auto port = fact->makePortItem(*audio_inlet, doc,
this,
this);
205 levels_outlet, &Process::ControlOutlet::valueChanged,
this,
206 [
this](
const ossia::value& v) {
207 if(
auto* list = v.target<std::vector<ossia::value>>())
209 const int entries = list->size();
211 const int num_channels = entries / 3;
212 m_channels.resize(num_channels);
213 for(int c = 0; c < num_channels; c++)
215 m_channels[c].peak = ossia::convert<float>((*list)[c * 3 + 0]);
216 m_channels[c].rms = ossia::convert<float>((*list)[c * 3 + 1]);
217 m_channels[c].peak_hold = ossia::convert<float>((*list)[c * 3 + 2]);
230 static QColor level_color(
double dB)
noexcept
234 return QColor(76, 175, 80);
238 const double t = (dB + 12.0) / 9.0;
240 76 +
static_cast<int>(179 * t),
241 175 +
static_cast<int>(80 * t - 80 * t * t * 0.3),
242 80 -
static_cast<int>(60 * t));
247 const double t = std::min(1.0, (dB + 3.0) / 3.0);
250 static_cast<int>(235 * (1.0 - t)),
251 static_cast<int>(20 * (1.0 - t)));
255 void paint_impl(QPainter* p)
const override
257 const int num_ch = m_channels.size();
261 const auto bounds = boundingRect();
262 const double total_h = bounds.height();
263 const double meter_h = total_h - top_margin - bottom_margin;
268 p->setRenderHint(QPainter::Antialiasing,
false);
270 const double start_x = label_width + 4.0;
271 const double avail_w = bounds.width() - start_x;
274 double sw = preferred_strip_width;
275 double sp = preferred_strip_spacing;
276 const double ideal_w = num_ch * sw + (num_ch - 1) * sp;
277 if(ideal_w > avail_w && num_ch > 0)
280 sp = min_strip_spacing;
281 sw = (avail_w - (num_ch - 1) * sp) / num_ch;
285 sw = avail_w / num_ch;
296 draw_scale(p, start_x - 2.0, meter_h);
298 const bool show_labels = sw >= min_width_for_labels;
301 for(
int c = 0; c < num_ch; c++)
303 const double x = start_x + c * (sw + sp);
304 draw_channel_strip(p, x, sw, meter_h, m_channels[c]);
308 p->setPen(QColor(180, 180, 180));
312 const auto label = QString::number(c);
314 QRectF(x, top_margin + meter_h + 1, sw, bottom_margin - 1),
315 Qt::AlignHCenter | Qt::AlignTop, label);
323 void draw_scale(QPainter* p,
double right_x,
double meter_h)
const
325 p->setPen(QColor(120, 120, 120));
330 static constexpr double dB_marks[]
331 = {0, -3, -6, -12, -18, -24, -36, -48, -60};
332 for(
double dB : dB_marks)
334 const double y = top_margin + dB_to_y(dB, meter_h, min_dB);
335 const auto label = (dB == 0) ? QStringLiteral(
" 0")
336 : QString::number(
static_cast<int>(dB));
339 QRectF(0, y - 5, right_x - 2, 10), Qt::AlignRight | Qt::AlignVCenter,
343 p->drawLine(QPointF(right_x - 1, y), QPointF(right_x + 1, y));
347 void draw_channel_strip(
348 QPainter* p,
double x,
double sw,
double meter_h,
349 const ChannelDisplay& ch)
const
353 QRectF(x, top_margin, sw, meter_h), QColor(20, 20, 20));
355 const double peak_dB = Node::to_dB(ch.peak);
356 const double rms_dB = Node::to_dB(ch.rms);
357 const double hold_dB = Node::to_dB(ch.peak_hold);
360 const double rms_w = std::max(1.0, sw - 2);
361 draw_level_bar(p, x + 1, meter_h, rms_dB, 0.6, rms_w);
366 const double peak_bar_w = std::max(1.0, sw - 6);
367 const double peak_x = x + 3;
368 draw_level_bar(p, peak_x, meter_h, peak_dB, 1.0, peak_bar_w);
373 draw_level_bar(p, x, meter_h, peak_dB, 1.0, sw);
379 const double hold_y = top_margin + dB_to_y(hold_dB, meter_h, min_dB);
380 p->setPen(Qt::NoPen);
381 const double hold_margin = std::min(1.0, sw * 0.1);
383 QRectF(x + hold_margin, hold_y - 1, sw - 2 * hold_margin, 2),
384 level_color(hold_dB));
389 QPainter* p,
double x,
double meter_h,
double level_dB,
double alpha,
392 if(level_dB <= min_dB)
396 static constexpr double segment_dB_step = 1.5;
397 double current_dB = min_dB;
399 p->setPen(Qt::NoPen);
400 while(current_dB < level_dB)
402 const double seg_top_dB = std::min(current_dB + segment_dB_step, level_dB);
403 const double y_bottom
404 = top_margin + dB_to_y(current_dB, meter_h, min_dB);
406 = top_margin + dB_to_y(seg_top_dB, meter_h, min_dB);
408 QColor col = level_color(seg_top_dB);
409 col.setAlphaF(alpha);
410 p->fillRect(QRectF(x, y_top, bar_w, y_bottom - y_top), col);
412 current_dB = seg_top_dB;
416 static double dB_to_y(
double dB,
double height,
double min_dB)
noexcept
418 const double clamped = std::clamp(dB, min_dB, 0.0);
419 return height * (1.0 - (clamped - min_dB) / (0.0 - min_dB));