diff --git a/src/blackgui/components/audiovolumecomponent.ui b/src/blackgui/components/audiovolumecomponent.ui
index 5df13a97e..2705ba128 100644
--- a/src/blackgui/components/audiovolumecomponent.ui
+++ b/src/blackgui/components/audiovolumecomponent.ui
@@ -162,7 +162,7 @@
-
-
+
0
0
@@ -179,17 +179,14 @@
-
-
-
- Qt::Horizontal
+
+
+
+ 0
+ 0
+
-
-
- 40
- 20
-
-
-
+
@@ -218,6 +215,14 @@
+
+
+ BlackGui::CLevelMeter
+ QWidget
+
+ 1
+
+
diff --git a/src/blackgui/levelmeter.cpp b/src/blackgui/levelmeter.cpp
new file mode 100644
index 000000000..85c2ed36a
--- /dev/null
+++ b/src/blackgui/levelmeter.cpp
@@ -0,0 +1,111 @@
+/* Copyright (C) 2013
+ * swift project Community / Contributors
+ *
+ * This file is part of swift project. It is subject to the license terms in the LICENSE file found in the top-level
+ * directory of this distribution and at http://www.swift-project.org/license.html. No part of swift project,
+ * including this file, may be copied, modified, propagated, or distributed except according to the terms
+ * contained in the LICENSE file.
+ *
+ * Class based on qt example: Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies)
+ */
+
+#include "levelmeter.h"
+
+#include
+
+#include
+#include
+#include
+
+
+namespace BlackGui
+{
+ CLevelMeter::CLevelMeter(QWidget *parent)
+ : QWidget(parent)
+ , m_peakDecayRate(PeakDecayRate)
+ , m_redrawTimer(new QTimer(this))
+ , m_rmsColor(Qt::red)
+ , m_peakColor(255, 200, 200, 255)
+ {
+ setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Preferred);
+ setMinimumWidth(30);
+
+ connect(m_redrawTimer, &QTimer::timeout, this, &CLevelMeter::ps_redrawTimerExpired);
+ m_redrawTimer->start(RedrawInterval);
+ }
+
+ CLevelMeter::~CLevelMeter()
+ { }
+
+ void CLevelMeter::reset()
+ {
+ m_rmsLevel = 0.0;
+ m_peakLevel = 0.0;
+ update();
+ }
+
+ void CLevelMeter::levelChanged(double rmsLevel, double peakLevel, int numSamples)
+ {
+ // Smooth the RMS signal
+ const double smooth = pow(double(0.9), static_cast(numSamples) / 256); // TODO: remove this magic number
+ m_rmsLevel = (m_rmsLevel * smooth) + (rmsLevel * (1.0 - smooth));
+
+ if (peakLevel > m_decayedPeakLevel)
+ {
+ m_peakLevel = peakLevel;
+ m_decayedPeakLevel = peakLevel;
+ m_peakLevelChanged.start();
+ }
+
+ if (peakLevel > m_peakHoldLevel)
+ {
+ m_peakHoldLevel = peakLevel;
+ m_peakHoldLevelChanged.start();
+ }
+
+ update();
+ }
+
+ void CLevelMeter::ps_redrawTimerExpired()
+ {
+ // Decay the peak signal
+ const int elapsedMs = m_peakLevelChanged.elapsed();
+ const double decayAmount = m_peakDecayRate * elapsedMs;
+ if (decayAmount < m_peakLevel)
+ {
+ m_decayedPeakLevel = m_peakLevel - decayAmount;
+ }
+ else
+ {
+ m_decayedPeakLevel = 0.0;
+ }
+
+ // Check whether to clear the peak hold level
+ if (m_peakHoldLevelChanged.elapsed() > PeakHoldLevelDuration)
+ m_peakHoldLevel = 0.0;
+
+ update();
+ }
+
+ void CLevelMeter::paintEvent(QPaintEvent *event)
+ {
+ Q_UNUSED(event)
+
+ QPainter painter(this);
+ painter.fillRect(rect(), Qt::black);
+
+ QRect bar = rect();
+
+ bar.setTop(rect().top() + (1.0 - m_peakHoldLevel) * rect().height());
+ bar.setBottom(bar.top() + 5);
+ painter.fillRect(bar, m_rmsColor);
+ bar.setBottom(rect().bottom());
+
+ bar.setTop(rect().top() + (1.0 - m_decayedPeakLevel) * rect().height());
+ painter.fillRect(bar, m_peakColor);
+
+ bar.setTop(rect().top() + (1.0 - m_rmsLevel) * rect().height());
+ painter.fillRect(bar, m_rmsColor);
+ }
+
+} // namespace
diff --git a/src/blackgui/levelmeter.h b/src/blackgui/levelmeter.h
new file mode 100644
index 000000000..499cfc98a
--- /dev/null
+++ b/src/blackgui/levelmeter.h
@@ -0,0 +1,85 @@
+/* Copyright (C) 2013
+ * swift project Community / Contributors
+ *
+ * This file is part of swift project. It is subject to the license terms in the LICENSE file found in the top-level
+ * directory of this distribution and at http://www.swift-project.org/license.html. No part of swift project,
+ * including this file, may be copied, modified, propagated, or distributed except according to the terms
+ * contained in the LICENSE file.
+ *
+ * Class based on qt example: Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies)
+ */
+
+//! \file
+
+#ifndef BLACKGUI_LEVELMETER_H
+#define BLACKGUI_LEVELMETER_H
+
+#include
+#include
+
+namespace BlackGui
+{
+
+ /**
+ * Widget which displays a vertical audio level meter, indicating the
+ * RMS and peak levels of the window of audio samples most recently analyzed
+ * by the Engine.
+ */
+ class CLevelMeter : public QWidget
+ {
+ Q_OBJECT
+ public:
+ //! Constructor
+ CLevelMeter(QWidget *parent = 0);
+
+ //! Destructor
+ ~CLevelMeter();
+
+ //! \copydoc QWidget::paintEvent
+ void paintEvent(QPaintEvent *event) override;
+
+ public slots:
+
+ //! Clean up
+ void reset();
+
+ //! Values
+ void levelChanged(double rmsLevel, double peakLevel, int numSamples);
+
+ private slots:
+ void ps_redrawTimerExpired();
+
+ private:
+ const int RedrawInterval = 100; // ms
+ const double PeakDecayRate = 0.001;
+ const int PeakHoldLevelDuration = 2000; // ms
+
+ //! Height of RMS level bar, range 0.0 - 1.0.
+ double m_rmsLevel = 0.0;
+
+ //! Most recent peak level, range 0.0 - 1.0.
+ double m_peakLevel = 0.0;
+
+ //! Height of peak level bar.
+ //! This is calculated by decaying m_peakLevel depending on the elapsed time since m_peakLevelChanged, and the value of m_decayRate.
+ double m_decayedPeakLevel = 0.0;
+
+ //! Time at which m_peakLevel was last changed.
+ QTime m_peakLevelChanged;
+
+ //! Rate at which peak level bar decays. Expressed in level units / millisecond.
+ double m_peakDecayRate;
+
+ //! High watermark of peak level. Range 0.0 - 1.0.
+ double m_peakHoldLevel = 0.0;
+
+ //! Time at which m_peakHoldLevel was last changed.
+ QTime m_peakHoldLevelChanged;
+
+ QTimer *m_redrawTimer;
+ QColor m_rmsColor;
+ QColor m_peakColor;
+ };
+}
+
+#endif // guard