kwave  18.07.70
LevelMeter.cpp
Go to the documentation of this file.
1 /*************************************************************************
2  LevelMeter.cpp - multi-track audio level meter
3  -------------------
4  original copyright : Copyright 2002 Rik Hemsley (rikkus) <rik@kde.org>
5 
6  begin : Mon Nov 17 2003
7  copyright : (C) 2003 by Thomas Eschenbacher
8  email : Thomas.Eschenbacher@gmx.de
9  ***************************************************************************/
10 
11 /***************************************************************************
12  * *
13  * This program is free software; you can redistribute it and/or modify *
14  * it under the terms of the GNU General Public License as published by *
15  * the Free Software Foundation; either version 2 of the License, or *
16  * (at your option) any later version. *
17  * *
18  ***************************************************************************/
19 
20 #include "config.h"
21 #include <math.h>
22 
23 #include <QApplication>
24 #include <QBrush>
25 #include <QColor>
26 #include <QFont>
27 #include <QImage>
28 #include <QPainter>
29 #include <QPixmap>
30 #include <QTimer>
31 
32 #include <KLocalizedString>
33 
34 #include "libkwave/Utils.h"
35 
36 #include "LevelMeter.h"
37 
39 static const float UPDATES_PER_SECOND = 8.0f;
40 
42 static const float F_FAST_RISE = (20.0f);
43 
45 static const float F_FAST_DECAY = (0.5f);
46 
48 static const float F_PEAK_RISE = (F_FAST_RISE);
49 
51 static const float F_PEAK_DECAY = (0.005f);
52 
53 //***************************************************************************
55  :QWidget(parent),
56  m_tracks(0), m_sample_rate(0), m_yf(), m_yp(),
57  m_fast_queue(), m_peak_queue(),
58  m_current_fast(), m_current_peak(), m_timer(),
59  m_color_low(Qt::green),
60  m_color_normal(Qt::yellow),
61  m_color_high(Qt::red)
62 {
63  setAttribute(Qt::WA_NoBackground);
64  m_timer = new QTimer(this);
65  Q_ASSERT(m_timer);
66  connect(m_timer, SIGNAL(timeout()),
67  this, SLOT(timedUpdate()));
68 }
69 
70 //***************************************************************************
72 {
73  setTracks(0);
74 }
75 
76 //***************************************************************************
77 void Kwave::LevelMeter::paintEvent(QPaintEvent *)
78 {
79  drawContents();
80 }
81 
82 //***************************************************************************
83 void Kwave::LevelMeter::resizeEvent(QResizeEvent *)
84 {
85  repaint();
86 }
87 
88 //***************************************************************************
89 void Kwave::LevelMeter::setTracks(unsigned int tracks)
90 {
91  if (Kwave::toInt(tracks) == m_tracks) return;
92  m_tracks = tracks;
93  reset(); // re-create all arrays etc.
94 }
95 
96 //***************************************************************************
98 {
99  if (qFuzzyCompare(static_cast<float>(rate), m_sample_rate)) return;
100  m_sample_rate = static_cast<float>(rate);
101 }
102 
103 //***************************************************************************
104 void Kwave::LevelMeter::updateTrack(unsigned int track,
105  const Kwave::SampleArray &buffer)
106 {
107  Q_ASSERT(Kwave::toInt(track) < m_tracks);
108  if (Kwave::toInt(track) >= m_tracks) return;
109 
110  // calculate the number of samples per update (approx)
111  const unsigned int samples = buffer.size();
112  const unsigned int samples_per_update = Kwave::toUint(
113  rintf(ceilf(m_sample_rate / UPDATES_PER_SECOND)));
114  unsigned int next_fraction = samples_per_update;
115  const unsigned int queue_depth = ((samples / samples_per_update) + 2);
116 
117  /* fast update: rise */
118  float Fg = F_FAST_RISE / m_sample_rate;
119  float n = 1.0f / tanf(float(M_PI) * Fg);
120  const float a0_fr = 1.0f / (1.0f + n);
121  const float b1_fr = (1.0f - n) / (1.0f + n);
122 
123  /* fast update: decay */
125  n = 1.0f / tanf(float(M_PI) * Fg);
126  const float a0_fd = 1.0f / (1.0f + n);
127  const float b1_fd = (1.0f - n) / (1.0f + n);
128 
129  /* peak value: rise */
130  Fg = F_PEAK_RISE / m_sample_rate;
131  n = 1.0f / tanf(float(M_PI) * Fg);
132  const float a0_pr = 1.0f / (1.0f + n);
133  const float b1_pr = (1.0f - n) / (1.0f + n);
134 
135  /* peak value: decay */
137  n = 1.0f / tanf(float(M_PI) * Fg);
138  const float a0_pd = 1.0f / (1.0f + n);
139  const float b1_pd = (1.0f - n) / (1.0f + n);
140 
141  float yf = m_yf[track];
142  float yp = m_yp[track];
143  float last_x = yf;
144  for (unsigned int t = 0; t < samples; ++t) {
145  float x = fabsf(sample2float(buffer[t])); /* rectifier */
146 
147  /* fast value */
148  if (x > yf) yf = (a0_fr * x) + (a0_fr * last_x) - (b1_fr * yf); // rise
149  yf = (a0_fd * x) + (a0_fd * last_x) - (b1_fd * yf); // decay
150 
151  /* peak value */
152  if (x > yp) yp = (a0_pr * x) + (a0_pr * last_x) - (b1_pr * yp); // rise
153  yp = (a0_pd * x) + (a0_pd * last_x) - (b1_pd * yp); // decay
154 
155  // remember x[t-1]
156  last_x = x;
157 
158  // enqueue new values if limit reached
159  if ((t > next_fraction) || (t == samples-1)) {
160  next_fraction += samples_per_update;
161 
162  // merge the last fractional part to the last normal part
163  if ((next_fraction + samples_per_update) > samples)
164  next_fraction = samples-1;
165 
166  enqueue(track, yf, yp, queue_depth);
167  }
168  }
169  m_yf[track] = yf;
170  m_yp[track] = yp;
171 }
172 
173 //***************************************************************************
175 {
176  if (m_timer && m_timer->isActive()) m_timer->stop();
177 
178  m_yf.resize(m_tracks);
179  m_yf.fill(0.0);
180  m_fast_queue.resize(m_tracks);
181  m_current_fast.resize(m_tracks);
182  m_current_fast.fill(0.0);
183 
184  m_yp.resize(m_tracks);
185  m_yp.fill(0.0);
186  m_peak_queue.resize(m_tracks);
187  m_current_peak.resize(m_tracks);
188  m_current_peak.fill(0.0);
189 }
190 
191 //***************************************************************************
192 void Kwave::LevelMeter::enqueue(unsigned int track, float fast, float peak,
193  unsigned int queue_depth)
194 {
195  Q_ASSERT(Kwave::toInt(track) < m_tracks);
196  Q_ASSERT(m_peak_queue.size() == m_fast_queue.size());
197  Q_ASSERT(m_fast_queue.size() >= m_tracks);
198  Q_ASSERT(m_peak_queue.size() >= m_tracks);
199  if ((Kwave::toInt(track) >= m_tracks) ||
200  (m_fast_queue.size() < Kwave::toInt(m_tracks)) ||
201  (m_peak_queue.size() < m_tracks)) return;
202  Q_ASSERT(m_peak_queue[track].size() == m_fast_queue[track].size());
203  if (m_peak_queue[track].size() != m_fast_queue[track].size()) return;
204 
205  // remove old entries
206  while (m_fast_queue[track].size() > Kwave::toInt(queue_depth)) {
207 // qDebug("LevelMeter::enqueue(): purging old entry (%u/%u)",
208 // m_fast_queue.size(), queue_depth);
209  m_fast_queue[track].dequeue();
210  m_peak_queue[track].dequeue();
211  }
212 
213  // put into the queue
214  m_fast_queue[track].enqueue(fast);
215  m_peak_queue[track].enqueue(peak);
216 
217  // restart the timer if necessary
218  if (m_timer && !m_timer->isActive()) {
219  m_timer->setInterval(Kwave::toInt(1000 / UPDATES_PER_SECOND));
220  m_timer->setSingleShot(false);
221  m_timer->start();
222  }
223 }
224 
225 //***************************************************************************
226 bool Kwave::LevelMeter::dequeue(unsigned int track, float &fast, float &peak)
227 {
228  Q_ASSERT(m_peak_queue.size() == m_fast_queue.size());
229  Q_ASSERT(m_fast_queue.size() >= m_tracks);
230  Q_ASSERT(m_peak_queue.size() >= m_tracks);
231  if ((Kwave::toInt(track) >= m_tracks) ||
232  (m_fast_queue.size() < m_tracks) ||
233  (m_peak_queue.size() < m_tracks)) return false;
234  Q_ASSERT(m_peak_queue[track].size() == m_fast_queue[track].size());
235  if (m_peak_queue[track].size() != m_fast_queue[track].size())
236  return false;
237 
238  // check if the queues are empty
239  if (m_fast_queue[track].isEmpty()) return false;
240  if (m_peak_queue[track].isEmpty()) return false;
241 
242  // get the values from the front of the queue
243  fast = m_fast_queue[track].dequeue();
244  peak = m_peak_queue[track].dequeue();
245 
246  return true;
247 }
248 
249 //***************************************************************************
251 {
252  float fast;
253  float peak;
254  bool need_update = false;
255 
256  for (int track=0; track < m_tracks; track++) {
257  if (dequeue(track, fast, peak)) {
258  // set the new "current" values
259  m_current_fast[track] = fast;
260  m_current_peak[track] = peak;
261 
262  // remember that we have to update the display
263  need_update = true;
264  }
265  }
266 
267  // refresh the display if needed
268  if (need_update) repaint();
269 }
270 
271 //***************************************************************************
273 {
274  // draw the levels in 3dB steps, like -12dB -9dB -6dB -3dB and 0dB
275  QFontMetrics fm = p.fontMetrics();
276  QRect rect = fm.boundingRect(i18n("%1 dB", -999));
277 
278  const int border = 4;
279  const int w = width() - 2 * border;
280  const int h = height();
281  const int tw = rect.width();
282  const int th = rect.height();
283  const int y = ((height() - th) / 2);
284  const int r = 5;
285  int db = 0;
286  int right = width();
287  const QColor textcolor = palette().buttonText().color();
288  const QBrush brush(palette().background().color());
289 
290  Q_ASSERT(th);
291  if (!th) return;
292 
293  p.setBrush(brush);
294  while (right > tw + border) {
295  // find the first position in dB which is not overlapping
296  // the last output position
297  QString txt;
298  int x;
299  do {
300  txt = i18n("%1 dB", db);
301  x = Kwave::toInt(static_cast<double>(w) *
302  pow(10.0, static_cast<double>(db) / 20.0));
303  db -= 3; // one step left == -3dB
304  } while ((x > right) && (x >= tw));
305  if (x < tw) break;
306 
307  // calculate the text position
308  int text_width = fm.boundingRect(txt).width();
309  x += border;
310  x -= text_width + 3;
311 
312  // dim the text background area
313  p.setOpacity(0.66);
314  p.setPen(Qt::NoPen);
315  p.drawRoundRect(x - r, y - r, text_width + 2 * r, th + 2 * r,
316  (200 * r) / th, (200 * r) / th);
317 
318  // draw the text, right/center aligned
319  p.setOpacity(1.0);
320  p.setPen(textcolor);
321  p.drawText(x, 1, text_width, h, Qt::AlignCenter, txt);
322 
323  // new right border == one character left from last one
324  right = x - th;
325  }
326 
327 }
328 
329 //***************************************************************************
330 /*
331  Original idea:
332  Copyright 2002 Rik Hemsley (rikkus) <rik@kde.org>
333 
334  Permission is hereby granted, free of charge, to any person obtaining a copy
335  of this software and associated documentation files (the "Software"), to
336  deal in the Software without restriction, including without limitation the
337  rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
338  sell copies of the Software, and to permit persons to whom the Software is
339  furnished to do so, subject to the following conditions:
340 
341  The above copyright notice and this permission notice shall be included in
342  all copies or substantial portions of the Software.
343 */
345 {
346  QPainter p;
347  int track;
348 
349  Q_ASSERT(width() > 0);
350  Q_ASSERT(height() > 0);
351 
352  p.begin(this);
353 
354  // fill the background
355  p.fillRect(rect(), palette().background().color());
356 
357  const unsigned int border = 4;
358  const unsigned int cell = 3;
359  const unsigned int w = width() - (border * 2) - (cell * 2);
360  const unsigned int h = (height() - border) / (m_tracks ? m_tracks : 1);
361 
362  const unsigned int w_low = Kwave::toUint(w * 0.7); // -3 dB
363  const unsigned int w_high = Kwave::toUint(w * 0.85); // -1.5dB
364 
365  for (track = 0; track < m_tracks; ++track) {
366  // show a bar up to the "fast" value
367  const unsigned int fast = Kwave::toUint(m_current_fast[track] * w);
368  for (unsigned int i = 0; i < w; i += cell * 2) {
369  QColor color;
370  if (i >= w_high)
371  color = m_color_high;
372  else if (i >= w_low)
373  color = m_color_normal;
374  else
375  color = m_color_low;
376 
377  p.fillRect(
378  border + cell + i,
379  border + (track * h),
380  cell, h-border,
381  (i > fast) ? color.dark() : color
382  );
383  }
384 
385  // draw the peak value
386  unsigned int peak = Kwave::toUint(m_current_peak[track] * w);
387  QColor peak_color;
388  if (peak >= w_high)
389  peak_color = m_color_high;
390  else if (peak >= w_low)
391  peak_color = m_color_normal;
392  else
393  peak_color = m_color_low;
394 
395  p.fillRect(
396  border + cell + peak,
397  border + (track * h),
398  cell, h - border,
399  peak_color.light()
400  );
401  }
402 
403  // draw the scale / dB numbers
404  drawScale(p);
405 
406  p.end();
407 }
408 
409 //***************************************************************************
410 //***************************************************************************
virtual void enqueue(unsigned int track, float fast, float peak, unsigned int queue_depth)
Definition: LevelMeter.cpp:192
static const float F_FAST_RISE
Definition: LevelMeter.cpp:42
virtual void drawContents()
Definition: LevelMeter.cpp:344
static const float UPDATES_PER_SECOND
Definition: LevelMeter.cpp:39
QVector< float > m_yp
Definition: LevelMeter.h:135
QVector< float > m_yf
Definition: LevelMeter.h:132
QColor m_color_normal
Definition: LevelMeter.h:156
QVector< float > m_current_fast
Definition: LevelMeter.h:144
virtual ~LevelMeter() Q_DECL_OVERRIDE
Definition: LevelMeter.cpp:71
bool connect(Kwave::StreamObject &source, const char *output, Kwave::StreamObject &sink, const char *input)
Definition: Connect.cpp:48
static const float F_FAST_DECAY
Definition: LevelMeter.cpp:45
virtual bool dequeue(unsigned int track, float &fast, float &peak)
Definition: LevelMeter.cpp:226
virtual void setSampleRate(double rate)
Definition: LevelMeter.cpp:97
static const float F_PEAK_DECAY
Definition: LevelMeter.cpp:51
QVector< QQueue< float > > m_peak_queue
Definition: LevelMeter.h:141
QVector< QQueue< float > > m_fast_queue
Definition: LevelMeter.h:138
QTimer * m_timer
Definition: LevelMeter.h:150
static const char * background[]
int toInt(T x)
Definition: Utils.h:127
virtual void resizeEvent(QResizeEvent *) Q_DECL_OVERRIDE
Definition: LevelMeter.cpp:83
virtual void setTracks(unsigned int tracks)
Definition: LevelMeter.cpp:89
LevelMeter(QWidget *parent)
Definition: LevelMeter.cpp:54
static float sample2float(const sample_t s)
Definition: Sample.h:65
virtual void timedUpdate()
Definition: LevelMeter.cpp:250
virtual void reset()
Definition: LevelMeter.cpp:174
virtual void updateTrack(unsigned int track, const Kwave::SampleArray &buffer)
Definition: LevelMeter.cpp:104
unsigned int size() const
unsigned int toUint(T x)
Definition: Utils.h:109
virtual void paintEvent(QPaintEvent *) Q_DECL_OVERRIDE
Definition: LevelMeter.cpp:77
static const float F_PEAK_RISE
Definition: LevelMeter.cpp:48
QVector< float > m_current_peak
Definition: LevelMeter.h:147
static double rect(double param)
Definition: Functions.cpp:29
void drawScale(QPainter &p)
Definition: LevelMeter.cpp:272