qt6-bb10/qtmultimedia/examples/multimedia/player/audiolevelmeter.h

144 lines
4.0 KiB
C++
Raw Permalink Blame History

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

// Copyright (C) 2025 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
#ifndef AUDIOLEVELMETER_H
#define AUDIOLEVELMETER_H
#include <QWidget>
#include <QThread>
#include <queue>
#include <chrono>
#include <QTimer>
#include <QAudioBuffer>
QT_BEGIN_NAMESPACE
class QToolButton;
class QLabel;
QT_END_NAMESPACE
using namespace std::chrono_literals;
using std::chrono::milliseconds;
// Constants used by AudioLevelMeter and MeterChannel
static constexpr int WIDGET_WIDTH = 34;
static constexpr int MAX_CHANNELS = 8;
static constexpr auto PEAK_COLOR = "#1F9B5D";
static constexpr auto RMS_COLOR = "#28C878";
static constexpr milliseconds RMS_WINDOW = 400ms;
static constexpr milliseconds PEAK_LABEL_HOLD_TIME = 2000ms;
static constexpr milliseconds DECAY_EASE_IN_TIME = 160ms;
static constexpr milliseconds UPDATE_INTERVAL = 16ms; // Assuming 60 Hz refresh rate.
static constexpr float DB_DECAY_PER_SECOND = 20.0f;
static constexpr float DB_DECAY_PER_UPDATE =
DB_DECAY_PER_SECOND / (1000 / UPDATE_INTERVAL.count());
static constexpr float DB_MAX = 0.0f;
static constexpr float DB_MIN = -60.0f;
// A struct used by BufferAnalyzer to emit its results back to AudioLevelMeter
struct BufferValues {
BufferValues(int nChannels) : peaks(nChannels, 0.0f), squares(nChannels, 0.0f) {}
std::vector<float> peaks;
std::vector<float> squares;
};
// A worker class analyzing incoming buffers on a separate worker thread
class BufferAnalyzer : public QObject
{
Q_OBJECT
public:
void requestStop() { m_stopRequested.store(true); }
public slots:
void analyzeBuffer(const QAudioBuffer &buffer, int maxChannelsToAnalyze);
signals:
void valuesReady(const BufferValues &values);
private:
std::atomic<bool> m_stopRequested = false;
};
// A custom QWidget representing an audio channel in the audio level meter. It serves
// both as a model for the channels's peak and RMS values and as a view using the overrided
// paintEvent()
class MeterChannel : public QWidget
{
Q_OBJECT
friend class AudioLevelMeter;
private:
explicit MeterChannel(QWidget *parent = nullptr);
void paintEvent(QPaintEvent *event) override;
void activate();
void deactivate();
void updatePeak(float peak);
void updateRms(float sumOfSquaresForOneBuffer, milliseconds duration, int frameCount);
void clearRmsData();
void decayPeak();
void decayRms();
float normalize(float dB);
float m_peakDecayRate = 0.0f;
float m_rmsDecayRate = 0.0f;
float m_peak = DB_MIN;
float m_rms = DB_MIN;
float m_sumOfSquares = 0.0f;
std::queue<float> m_sumOfSquaresQueue{};
QBrush m_peakBrush{PEAK_COLOR};
QBrush m_rmsBrush{RMS_COLOR};
};
// The audio level meter´s parent widget class. It acts as a controller
// for the MeterChannel widgets and the BufferAnalyzer worker.
class AudioLevelMeter : public QWidget
{
Q_OBJECT
public:
explicit AudioLevelMeter(QWidget *parent = nullptr);
~AudioLevelMeter() {
m_analyzerThread.requestInterruption();
m_bufferAnalyzer->requestStop();
m_analyzerThread.quit();
m_analyzerThread.wait();
};
signals:
void newBuffer(const QAudioBuffer &buffer, int nChannels);
public slots:
void onAudioBufferReceived(const QAudioBuffer &buffer);
void deactivate();
private slots:
void toggleOnOff();
void updateValues(const BufferValues &values);
void updateBars();
void resetPeakLabel();
private:
void activate();
void updateChannelCount(int nCh);
void updatePeakLabel(float peak);
void clearAllRmsData();
bool m_isOn = true;
bool m_isActive = false;
QList<MeterChannel *> m_channels{MAX_CHANNELS};
int m_channelCount = 0;
milliseconds m_bufferDurationMs = 0ms;
int m_frameCount = 0;
float m_highestPeak = 0.0f;
QTimer m_updateTimer;
QTimer m_deactivationTimer;
QTimer m_peakLabelHoldTimer;
QLabel *m_peakLabel = nullptr;
QToolButton *m_onOffButton = nullptr;
BufferAnalyzer *m_bufferAnalyzer = nullptr;
QThread m_analyzerThread;
};
#endif // AUDIOLEVELMETER_H