177 static constexpr double min_dB = -60.0;
178 static constexpr double preferred_strip_width = 16.0;
179 static constexpr double min_strip_spacing = 1.0;
180 static constexpr double preferred_strip_spacing = 3.0;
181 static constexpr double label_width = 30.0;
182 static constexpr double top_margin = 4.0;
183 static constexpr double bottom_margin = 12.0;
185 static constexpr double min_width_for_labels = 8.0;
192 float peak_hold = 0.f;
194 std::vector<ChannelDisplay> m_channels;
198 QGraphicsItem* parent)
199 :
Process::EffectLayerView{parent}
201 setAcceptedMouseButtons({});
206 auto* audio_inlet = process.inlets().front();
207 auto fact = portFactory.get(audio_inlet->concreteKey());
208 auto port = fact->makePortItem(*audio_inlet, doc,
this,
this);
215 levels_outlet, &Process::ControlOutlet::valueChanged,
this,
216 [
this](
const ossia::value& v) {
217 if(
auto* list = v.target<std::vector<ossia::value>>())
219 const int entries = list->size();
221 const int num_channels = entries / 3;
222 m_channels.resize(num_channels);
223 for(int c = 0; c < num_channels; c++)
225 m_channels[c].peak = ossia::convert<float>((*list)[c * 3 + 0]);
226 m_channels[c].rms = ossia::convert<float>((*list)[c * 3 + 1]);
227 m_channels[c].peak_hold = ossia::convert<float>((*list)[c * 3 + 2]);
240 static QColor level_color(
double dB)
noexcept
244 return QColor(76, 175, 80);
248 const double t = (dB + 12.0) / 9.0;
250 76 +
static_cast<int>(179 * t),
251 175 +
static_cast<int>(80 * t - 80 * t * t * 0.3),
252 80 -
static_cast<int>(60 * t));
257 const double t = std::min(1.0, (dB + 3.0) / 3.0);
260 static_cast<int>(235 * (1.0 - t)),
261 static_cast<int>(20 * (1.0 - t)));
265 void paint_impl(QPainter* p)
const override
267 const int num_ch = m_channels.size();
271 const auto bounds = boundingRect();
272 const double total_h = bounds.height();
273 const double meter_h = total_h - top_margin - bottom_margin;
278 p->setRenderHint(QPainter::Antialiasing,
false);
280 const double start_x = label_width + 4.0;
281 const double avail_w = bounds.width() - start_x;
284 double sw = preferred_strip_width;
285 double sp = preferred_strip_spacing;
286 const double ideal_w = num_ch * sw + (num_ch - 1) * sp;
287 if(ideal_w > avail_w && num_ch > 0)
290 sp = min_strip_spacing;
291 sw = (avail_w - (num_ch - 1) * sp) / num_ch;
295 sw = avail_w / num_ch;
306 draw_scale(p, start_x - 2.0, meter_h);
308 const bool show_labels = sw >= min_width_for_labels;
311 for(
int c = 0; c < num_ch; c++)
313 const double x = start_x + c * (sw + sp);
314 draw_channel_strip(p, x, sw, meter_h, m_channels[c]);
318 p->setPen(QColor(180, 180, 180));
322 const auto label = QString::number(c);
324 QRectF(x, top_margin + meter_h + 1, sw, bottom_margin - 1),
325 Qt::AlignHCenter | Qt::AlignTop, label);
333 void draw_scale(QPainter* p,
double right_x,
double meter_h)
const
335 p->setPen(QColor(120, 120, 120));
340 static constexpr double dB_marks[]
341 = {0, -3, -6, -12, -18, -24, -36, -48, -60};
342 for(
double dB : dB_marks)
344 const double y = top_margin + dB_to_y(dB, meter_h, min_dB);
345 const auto label = (dB == 0) ? QStringLiteral(
" 0")
346 : QString::number(
static_cast<int>(dB));
349 QRectF(0, y - 5, right_x - 2, 10), Qt::AlignRight | Qt::AlignVCenter,
353 p->drawLine(QPointF(right_x - 1, y), QPointF(right_x + 1, y));
357 void draw_channel_strip(
358 QPainter* p,
double x,
double sw,
double meter_h,
359 const ChannelDisplay& ch)
const
363 QRectF(x, top_margin, sw, meter_h), QColor(20, 20, 20));
365 const double peak_dB = Node::to_dB(ch.peak);
366 const double rms_dB = Node::to_dB(ch.rms);
367 const double hold_dB = Node::to_dB(ch.peak_hold);
370 const double rms_w = std::max(1.0, sw - 2);
371 draw_level_bar(p, x + 1, meter_h, rms_dB, 0.6, rms_w);
376 const double peak_bar_w = std::max(1.0, sw - 6);
377 const double peak_x = x + 3;
378 draw_level_bar(p, peak_x, meter_h, peak_dB, 1.0, peak_bar_w);
383 draw_level_bar(p, x, meter_h, peak_dB, 1.0, sw);
389 const double hold_y = top_margin + dB_to_y(hold_dB, meter_h, min_dB);
390 p->setPen(Qt::NoPen);
391 const double hold_margin = std::min(1.0, sw * 0.1);
393 QRectF(x + hold_margin, hold_y - 1, sw - 2 * hold_margin, 2),
394 level_color(hold_dB));
399 QPainter* p,
double x,
double meter_h,
double level_dB,
double alpha,
402 if(level_dB <= min_dB)
406 static constexpr double segment_dB_step = 1.5;
407 double current_dB = min_dB;
409 p->setPen(Qt::NoPen);
410 while(current_dB < level_dB)
412 const double seg_top_dB = std::min(current_dB + segment_dB_step, level_dB);
413 const double y_bottom
414 = top_margin + dB_to_y(current_dB, meter_h, min_dB);
416 = top_margin + dB_to_y(seg_top_dB, meter_h, min_dB);
418 QColor col = level_color(seg_top_dB);
419 col.setAlphaF(alpha);
420 p->fillRect(QRectF(x, y_top, bar_w, y_bottom - y_top), col);
422 current_dB = seg_top_dB;
426 static double dB_to_y(
double dB,
double height,
double min_dB)
noexcept
428 const double clamped = std::clamp(dB, min_dB, 0.0);
429 return height * (1.0 - (clamped - min_dB) / (0.0 - min_dB));