frontend: Prepare Qt UI Widgets for splits
This commit is contained in:
parent
f813121bb9
commit
9f887c76d3
2541
frontend/utility/AdvancedOutput.hpp
Normal file
2541
frontend/utility/AdvancedOutput.hpp
Normal file
File diff suppressed because it is too large
Load Diff
2541
frontend/utility/BasicOutputHandler.cpp
Normal file
2541
frontend/utility/BasicOutputHandler.cpp
Normal file
File diff suppressed because it is too large
Load Diff
1382
frontend/utility/SceneRenameDelegate.hpp
Normal file
1382
frontend/utility/SceneRenameDelegate.hpp
Normal file
File diff suppressed because it is too large
Load Diff
2541
frontend/utility/SimpleOutput.cpp
Normal file
2541
frontend/utility/SimpleOutput.cpp
Normal file
File diff suppressed because it is too large
Load Diff
2541
frontend/utility/SimpleOutput.hpp
Normal file
2541
frontend/utility/SimpleOutput.hpp
Normal file
File diff suppressed because it is too large
Load Diff
2541
frontend/utility/StartMultiTrackVideoStreamingGuard.hpp
Normal file
2541
frontend/utility/StartMultiTrackVideoStreamingGuard.hpp
Normal file
File diff suppressed because it is too large
Load Diff
1507
frontend/utility/VolumeMeterTimer.cpp
Normal file
1507
frontend/utility/VolumeMeterTimer.cpp
Normal file
File diff suppressed because it is too large
Load Diff
341
frontend/utility/VolumeMeterTimer.hpp
Normal file
341
frontend/utility/VolumeMeterTimer.hpp
Normal file
@ -0,0 +1,341 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <obs.hpp>
|
||||||
|
#include <QWidget>
|
||||||
|
#include <QPaintEvent>
|
||||||
|
#include <QTimer>
|
||||||
|
#include <QMutex>
|
||||||
|
#include <QList>
|
||||||
|
#include <QMenu>
|
||||||
|
#include <QAccessibleWidget>
|
||||||
|
#include "absolute-slider.hpp"
|
||||||
|
|
||||||
|
class QPushButton;
|
||||||
|
class VolumeMeterTimer;
|
||||||
|
class VolumeSlider;
|
||||||
|
|
||||||
|
class VolumeMeter : public QWidget {
|
||||||
|
Q_OBJECT
|
||||||
|
Q_PROPERTY(QColor backgroundNominalColor READ getBackgroundNominalColor WRITE setBackgroundNominalColor
|
||||||
|
DESIGNABLE true)
|
||||||
|
Q_PROPERTY(QColor backgroundWarningColor READ getBackgroundWarningColor WRITE setBackgroundWarningColor
|
||||||
|
DESIGNABLE true)
|
||||||
|
Q_PROPERTY(
|
||||||
|
QColor backgroundErrorColor READ getBackgroundErrorColor WRITE setBackgroundErrorColor DESIGNABLE true)
|
||||||
|
Q_PROPERTY(QColor foregroundNominalColor READ getForegroundNominalColor WRITE setForegroundNominalColor
|
||||||
|
DESIGNABLE true)
|
||||||
|
Q_PROPERTY(QColor foregroundWarningColor READ getForegroundWarningColor WRITE setForegroundWarningColor
|
||||||
|
DESIGNABLE true)
|
||||||
|
Q_PROPERTY(
|
||||||
|
QColor foregroundErrorColor READ getForegroundErrorColor WRITE setForegroundErrorColor DESIGNABLE true)
|
||||||
|
|
||||||
|
Q_PROPERTY(QColor backgroundNominalColorDisabled READ getBackgroundNominalColorDisabled WRITE
|
||||||
|
setBackgroundNominalColorDisabled DESIGNABLE true)
|
||||||
|
Q_PROPERTY(QColor backgroundWarningColorDisabled READ getBackgroundWarningColorDisabled WRITE
|
||||||
|
setBackgroundWarningColorDisabled DESIGNABLE true)
|
||||||
|
Q_PROPERTY(QColor backgroundErrorColorDisabled READ getBackgroundErrorColorDisabled WRITE
|
||||||
|
setBackgroundErrorColorDisabled DESIGNABLE true)
|
||||||
|
Q_PROPERTY(QColor foregroundNominalColorDisabled READ getForegroundNominalColorDisabled WRITE
|
||||||
|
setForegroundNominalColorDisabled DESIGNABLE true)
|
||||||
|
Q_PROPERTY(QColor foregroundWarningColorDisabled READ getForegroundWarningColorDisabled WRITE
|
||||||
|
setForegroundWarningColorDisabled DESIGNABLE true)
|
||||||
|
Q_PROPERTY(QColor foregroundErrorColorDisabled READ getForegroundErrorColorDisabled WRITE
|
||||||
|
setForegroundErrorColorDisabled DESIGNABLE true)
|
||||||
|
|
||||||
|
Q_PROPERTY(QColor clipColor READ getClipColor WRITE setClipColor DESIGNABLE true)
|
||||||
|
Q_PROPERTY(QColor magnitudeColor READ getMagnitudeColor WRITE setMagnitudeColor DESIGNABLE true)
|
||||||
|
Q_PROPERTY(QColor majorTickColor READ getMajorTickColor WRITE setMajorTickColor DESIGNABLE true)
|
||||||
|
Q_PROPERTY(QColor minorTickColor READ getMinorTickColor WRITE setMinorTickColor DESIGNABLE true)
|
||||||
|
Q_PROPERTY(int meterThickness READ getMeterThickness WRITE setMeterThickness DESIGNABLE true)
|
||||||
|
Q_PROPERTY(qreal meterFontScaling READ getMeterFontScaling WRITE setMeterFontScaling DESIGNABLE true)
|
||||||
|
|
||||||
|
// Levels are denoted in dBFS.
|
||||||
|
Q_PROPERTY(qreal minimumLevel READ getMinimumLevel WRITE setMinimumLevel DESIGNABLE true)
|
||||||
|
Q_PROPERTY(qreal warningLevel READ getWarningLevel WRITE setWarningLevel DESIGNABLE true)
|
||||||
|
Q_PROPERTY(qreal errorLevel READ getErrorLevel WRITE setErrorLevel DESIGNABLE true)
|
||||||
|
Q_PROPERTY(qreal clipLevel READ getClipLevel WRITE setClipLevel DESIGNABLE true)
|
||||||
|
Q_PROPERTY(qreal minimumInputLevel READ getMinimumInputLevel WRITE setMinimumInputLevel DESIGNABLE true)
|
||||||
|
|
||||||
|
// Rates are denoted in dB/second.
|
||||||
|
Q_PROPERTY(qreal peakDecayRate READ getPeakDecayRate WRITE setPeakDecayRate DESIGNABLE true)
|
||||||
|
|
||||||
|
// Time in seconds for the VU meter to integrate over.
|
||||||
|
Q_PROPERTY(qreal magnitudeIntegrationTime READ getMagnitudeIntegrationTime WRITE setMagnitudeIntegrationTime
|
||||||
|
DESIGNABLE true)
|
||||||
|
|
||||||
|
// Duration is denoted in seconds.
|
||||||
|
Q_PROPERTY(qreal peakHoldDuration READ getPeakHoldDuration WRITE setPeakHoldDuration DESIGNABLE true)
|
||||||
|
Q_PROPERTY(qreal inputPeakHoldDuration READ getInputPeakHoldDuration WRITE setInputPeakHoldDuration
|
||||||
|
DESIGNABLE true)
|
||||||
|
|
||||||
|
friend class VolControl;
|
||||||
|
|
||||||
|
private:
|
||||||
|
obs_volmeter_t *obs_volmeter;
|
||||||
|
static std::weak_ptr<VolumeMeterTimer> updateTimer;
|
||||||
|
std::shared_ptr<VolumeMeterTimer> updateTimerRef;
|
||||||
|
|
||||||
|
inline void resetLevels();
|
||||||
|
inline void doLayout();
|
||||||
|
inline bool detectIdle(uint64_t ts);
|
||||||
|
inline void calculateBallistics(uint64_t ts, qreal timeSinceLastRedraw = 0.0);
|
||||||
|
inline void calculateBallisticsForChannel(int channelNr, uint64_t ts, qreal timeSinceLastRedraw);
|
||||||
|
|
||||||
|
inline int convertToInt(float number);
|
||||||
|
void paintInputMeter(QPainter &painter, int x, int y, int width, int height, float peakHold);
|
||||||
|
void paintHMeter(QPainter &painter, int x, int y, int width, int height, float magnitude, float peak,
|
||||||
|
float peakHold);
|
||||||
|
void paintHTicks(QPainter &painter, int x, int y, int width);
|
||||||
|
void paintVMeter(QPainter &painter, int x, int y, int width, int height, float magnitude, float peak,
|
||||||
|
float peakHold);
|
||||||
|
void paintVTicks(QPainter &painter, int x, int y, int height);
|
||||||
|
|
||||||
|
QMutex dataMutex;
|
||||||
|
|
||||||
|
bool recalculateLayout = true;
|
||||||
|
uint64_t currentLastUpdateTime = 0;
|
||||||
|
float currentMagnitude[MAX_AUDIO_CHANNELS];
|
||||||
|
float currentPeak[MAX_AUDIO_CHANNELS];
|
||||||
|
float currentInputPeak[MAX_AUDIO_CHANNELS];
|
||||||
|
|
||||||
|
int displayNrAudioChannels = 0;
|
||||||
|
float displayMagnitude[MAX_AUDIO_CHANNELS];
|
||||||
|
float displayPeak[MAX_AUDIO_CHANNELS];
|
||||||
|
float displayPeakHold[MAX_AUDIO_CHANNELS];
|
||||||
|
uint64_t displayPeakHoldLastUpdateTime[MAX_AUDIO_CHANNELS];
|
||||||
|
float displayInputPeakHold[MAX_AUDIO_CHANNELS];
|
||||||
|
uint64_t displayInputPeakHoldLastUpdateTime[MAX_AUDIO_CHANNELS];
|
||||||
|
|
||||||
|
QFont tickFont;
|
||||||
|
QColor backgroundNominalColor;
|
||||||
|
QColor backgroundWarningColor;
|
||||||
|
QColor backgroundErrorColor;
|
||||||
|
QColor foregroundNominalColor;
|
||||||
|
QColor foregroundWarningColor;
|
||||||
|
QColor foregroundErrorColor;
|
||||||
|
|
||||||
|
QColor backgroundNominalColorDisabled;
|
||||||
|
QColor backgroundWarningColorDisabled;
|
||||||
|
QColor backgroundErrorColorDisabled;
|
||||||
|
QColor foregroundNominalColorDisabled;
|
||||||
|
QColor foregroundWarningColorDisabled;
|
||||||
|
QColor foregroundErrorColorDisabled;
|
||||||
|
|
||||||
|
QColor clipColor;
|
||||||
|
QColor magnitudeColor;
|
||||||
|
QColor majorTickColor;
|
||||||
|
QColor minorTickColor;
|
||||||
|
|
||||||
|
int meterThickness;
|
||||||
|
qreal meterFontScaling;
|
||||||
|
|
||||||
|
qreal minimumLevel;
|
||||||
|
qreal warningLevel;
|
||||||
|
qreal errorLevel;
|
||||||
|
qreal clipLevel;
|
||||||
|
qreal minimumInputLevel;
|
||||||
|
qreal peakDecayRate;
|
||||||
|
qreal magnitudeIntegrationTime;
|
||||||
|
qreal peakHoldDuration;
|
||||||
|
qreal inputPeakHoldDuration;
|
||||||
|
|
||||||
|
QColor p_backgroundNominalColor;
|
||||||
|
QColor p_backgroundWarningColor;
|
||||||
|
QColor p_backgroundErrorColor;
|
||||||
|
QColor p_foregroundNominalColor;
|
||||||
|
QColor p_foregroundWarningColor;
|
||||||
|
QColor p_foregroundErrorColor;
|
||||||
|
|
||||||
|
uint64_t lastRedrawTime = 0;
|
||||||
|
int channels = 0;
|
||||||
|
bool clipping = false;
|
||||||
|
bool vertical;
|
||||||
|
bool muted = false;
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit VolumeMeter(QWidget *parent = nullptr, obs_volmeter_t *obs_volmeter = nullptr, bool vertical = false);
|
||||||
|
~VolumeMeter();
|
||||||
|
|
||||||
|
void setLevels(const float magnitude[MAX_AUDIO_CHANNELS], const float peak[MAX_AUDIO_CHANNELS],
|
||||||
|
const float inputPeak[MAX_AUDIO_CHANNELS]);
|
||||||
|
QRect getBarRect() const;
|
||||||
|
bool needLayoutChange();
|
||||||
|
|
||||||
|
QColor getBackgroundNominalColor() const;
|
||||||
|
void setBackgroundNominalColor(QColor c);
|
||||||
|
QColor getBackgroundWarningColor() const;
|
||||||
|
void setBackgroundWarningColor(QColor c);
|
||||||
|
QColor getBackgroundErrorColor() const;
|
||||||
|
void setBackgroundErrorColor(QColor c);
|
||||||
|
QColor getForegroundNominalColor() const;
|
||||||
|
void setForegroundNominalColor(QColor c);
|
||||||
|
QColor getForegroundWarningColor() const;
|
||||||
|
void setForegroundWarningColor(QColor c);
|
||||||
|
QColor getForegroundErrorColor() const;
|
||||||
|
void setForegroundErrorColor(QColor c);
|
||||||
|
|
||||||
|
QColor getBackgroundNominalColorDisabled() const;
|
||||||
|
void setBackgroundNominalColorDisabled(QColor c);
|
||||||
|
QColor getBackgroundWarningColorDisabled() const;
|
||||||
|
void setBackgroundWarningColorDisabled(QColor c);
|
||||||
|
QColor getBackgroundErrorColorDisabled() const;
|
||||||
|
void setBackgroundErrorColorDisabled(QColor c);
|
||||||
|
QColor getForegroundNominalColorDisabled() const;
|
||||||
|
void setForegroundNominalColorDisabled(QColor c);
|
||||||
|
QColor getForegroundWarningColorDisabled() const;
|
||||||
|
void setForegroundWarningColorDisabled(QColor c);
|
||||||
|
QColor getForegroundErrorColorDisabled() const;
|
||||||
|
void setForegroundErrorColorDisabled(QColor c);
|
||||||
|
|
||||||
|
QColor getClipColor() const;
|
||||||
|
void setClipColor(QColor c);
|
||||||
|
QColor getMagnitudeColor() const;
|
||||||
|
void setMagnitudeColor(QColor c);
|
||||||
|
QColor getMajorTickColor() const;
|
||||||
|
void setMajorTickColor(QColor c);
|
||||||
|
QColor getMinorTickColor() const;
|
||||||
|
void setMinorTickColor(QColor c);
|
||||||
|
int getMeterThickness() const;
|
||||||
|
void setMeterThickness(int v);
|
||||||
|
qreal getMeterFontScaling() const;
|
||||||
|
void setMeterFontScaling(qreal v);
|
||||||
|
qreal getMinimumLevel() const;
|
||||||
|
void setMinimumLevel(qreal v);
|
||||||
|
qreal getWarningLevel() const;
|
||||||
|
void setWarningLevel(qreal v);
|
||||||
|
qreal getErrorLevel() const;
|
||||||
|
void setErrorLevel(qreal v);
|
||||||
|
qreal getClipLevel() const;
|
||||||
|
void setClipLevel(qreal v);
|
||||||
|
qreal getMinimumInputLevel() const;
|
||||||
|
void setMinimumInputLevel(qreal v);
|
||||||
|
qreal getPeakDecayRate() const;
|
||||||
|
void setPeakDecayRate(qreal v);
|
||||||
|
qreal getMagnitudeIntegrationTime() const;
|
||||||
|
void setMagnitudeIntegrationTime(qreal v);
|
||||||
|
qreal getPeakHoldDuration() const;
|
||||||
|
void setPeakHoldDuration(qreal v);
|
||||||
|
qreal getInputPeakHoldDuration() const;
|
||||||
|
void setInputPeakHoldDuration(qreal v);
|
||||||
|
void setPeakMeterType(enum obs_peak_meter_type peakMeterType);
|
||||||
|
virtual void mousePressEvent(QMouseEvent *event) override;
|
||||||
|
virtual void wheelEvent(QWheelEvent *event) override;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void paintEvent(QPaintEvent *event) override;
|
||||||
|
void changeEvent(QEvent *e) override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class VolumeMeterTimer : public QTimer {
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
inline VolumeMeterTimer() : QTimer() {}
|
||||||
|
|
||||||
|
void AddVolControl(VolumeMeter *meter);
|
||||||
|
void RemoveVolControl(VolumeMeter *meter);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void timerEvent(QTimerEvent *event) override;
|
||||||
|
QList<VolumeMeter *> volumeMeters;
|
||||||
|
};
|
||||||
|
|
||||||
|
class QLabel;
|
||||||
|
class VolumeSlider;
|
||||||
|
class MuteCheckBox;
|
||||||
|
class OBSSourceLabel;
|
||||||
|
|
||||||
|
class VolControl : public QFrame {
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
private:
|
||||||
|
OBSSource source;
|
||||||
|
std::vector<OBSSignal> sigs;
|
||||||
|
OBSSourceLabel *nameLabel;
|
||||||
|
QLabel *volLabel;
|
||||||
|
VolumeMeter *volMeter;
|
||||||
|
VolumeSlider *slider;
|
||||||
|
MuteCheckBox *mute;
|
||||||
|
QPushButton *config = nullptr;
|
||||||
|
float levelTotal;
|
||||||
|
float levelCount;
|
||||||
|
OBSFader obs_fader;
|
||||||
|
OBSVolMeter obs_volmeter;
|
||||||
|
bool vertical;
|
||||||
|
QMenu *contextMenu;
|
||||||
|
|
||||||
|
static void OBSVolumeChanged(void *param, float db);
|
||||||
|
static void OBSVolumeLevel(void *data, const float magnitude[MAX_AUDIO_CHANNELS],
|
||||||
|
const float peak[MAX_AUDIO_CHANNELS], const float inputPeak[MAX_AUDIO_CHANNELS]);
|
||||||
|
static void OBSVolumeMuted(void *data, calldata_t *calldata);
|
||||||
|
static void OBSMixersOrMonitoringChanged(void *data, calldata_t *);
|
||||||
|
|
||||||
|
void EmitConfigClicked();
|
||||||
|
|
||||||
|
private slots:
|
||||||
|
void VolumeChanged();
|
||||||
|
void VolumeMuted(bool muted);
|
||||||
|
void MixersOrMonitoringChanged();
|
||||||
|
|
||||||
|
void SetMuted(bool checked);
|
||||||
|
void SliderChanged(int vol);
|
||||||
|
void updateText();
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void ConfigClicked();
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit VolControl(OBSSource source, bool showConfig = false, bool vertical = false);
|
||||||
|
~VolControl();
|
||||||
|
|
||||||
|
inline obs_source_t *GetSource() const { return source; }
|
||||||
|
|
||||||
|
void SetMeterDecayRate(qreal q);
|
||||||
|
void setPeakMeterType(enum obs_peak_meter_type peakMeterType);
|
||||||
|
|
||||||
|
void EnableSlider(bool enable);
|
||||||
|
inline void SetContextMenu(QMenu *cm) { contextMenu = cm; }
|
||||||
|
|
||||||
|
void refreshColors();
|
||||||
|
};
|
||||||
|
|
||||||
|
class VolumeSlider : public AbsoluteSlider {
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
obs_fader_t *fad;
|
||||||
|
|
||||||
|
VolumeSlider(obs_fader_t *fader, QWidget *parent = nullptr);
|
||||||
|
VolumeSlider(obs_fader_t *fader, Qt::Orientation orientation, QWidget *parent = nullptr);
|
||||||
|
|
||||||
|
bool getDisplayTicks() const;
|
||||||
|
void setDisplayTicks(bool display);
|
||||||
|
|
||||||
|
private:
|
||||||
|
bool displayTicks = false;
|
||||||
|
QColor tickColor;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
virtual void paintEvent(QPaintEvent *event) override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class VolumeAccessibleInterface : public QAccessibleWidget {
|
||||||
|
|
||||||
|
public:
|
||||||
|
VolumeAccessibleInterface(QWidget *w);
|
||||||
|
|
||||||
|
QVariant currentValue() const;
|
||||||
|
void setCurrentValue(const QVariant &value);
|
||||||
|
|
||||||
|
QVariant maximumValue() const;
|
||||||
|
QVariant minimumValue() const;
|
||||||
|
|
||||||
|
QVariant minimumStepSize() const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
VolumeSlider *slider() const;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
virtual QAccessible::Role role() const override;
|
||||||
|
virtual QString text(QAccessible::Text t) const override;
|
||||||
|
};
|
10203
frontend/widgets/ColorSelect.cpp
Normal file
10203
frontend/widgets/ColorSelect.cpp
Normal file
File diff suppressed because it is too large
Load Diff
1382
frontend/widgets/ColorSelect.hpp
Normal file
1382
frontend/widgets/ColorSelect.hpp
Normal file
File diff suppressed because it is too large
Load Diff
10203
frontend/widgets/OBSBasic.cpp
Normal file
10203
frontend/widgets/OBSBasic.cpp
Normal file
File diff suppressed because it is too large
Load Diff
1382
frontend/widgets/OBSBasic.hpp
Normal file
1382
frontend/widgets/OBSBasic.hpp
Normal file
File diff suppressed because it is too large
Load Diff
10203
frontend/widgets/OBSBasic_Clipboard.cpp
Normal file
10203
frontend/widgets/OBSBasic_Clipboard.cpp
Normal file
File diff suppressed because it is too large
Load Diff
10203
frontend/widgets/OBSBasic_ContextToolbar.cpp
Normal file
10203
frontend/widgets/OBSBasic_ContextToolbar.cpp
Normal file
File diff suppressed because it is too large
Load Diff
10203
frontend/widgets/OBSBasic_Docks.cpp
Normal file
10203
frontend/widgets/OBSBasic_Docks.cpp
Normal file
File diff suppressed because it is too large
Load Diff
10203
frontend/widgets/OBSBasic_Hotkeys.cpp
Normal file
10203
frontend/widgets/OBSBasic_Hotkeys.cpp
Normal file
File diff suppressed because it is too large
Load Diff
10203
frontend/widgets/OBSBasic_MainControls.cpp
Normal file
10203
frontend/widgets/OBSBasic_MainControls.cpp
Normal file
File diff suppressed because it is too large
Load Diff
10203
frontend/widgets/OBSBasic_OutputHandler.cpp
Normal file
10203
frontend/widgets/OBSBasic_OutputHandler.cpp
Normal file
File diff suppressed because it is too large
Load Diff
10203
frontend/widgets/OBSBasic_Preview.cpp
Normal file
10203
frontend/widgets/OBSBasic_Preview.cpp
Normal file
File diff suppressed because it is too large
Load Diff
10203
frontend/widgets/OBSBasic_Projectors.cpp
Normal file
10203
frontend/widgets/OBSBasic_Projectors.cpp
Normal file
File diff suppressed because it is too large
Load Diff
10203
frontend/widgets/OBSBasic_Recording.cpp
Normal file
10203
frontend/widgets/OBSBasic_Recording.cpp
Normal file
File diff suppressed because it is too large
Load Diff
10203
frontend/widgets/OBSBasic_ReplayBuffer.cpp
Normal file
10203
frontend/widgets/OBSBasic_ReplayBuffer.cpp
Normal file
File diff suppressed because it is too large
Load Diff
10203
frontend/widgets/OBSBasic_SceneCollections.cpp
Normal file
10203
frontend/widgets/OBSBasic_SceneCollections.cpp
Normal file
File diff suppressed because it is too large
Load Diff
10203
frontend/widgets/OBSBasic_SceneItems.cpp
Normal file
10203
frontend/widgets/OBSBasic_SceneItems.cpp
Normal file
File diff suppressed because it is too large
Load Diff
10203
frontend/widgets/OBSBasic_Scenes.cpp
Normal file
10203
frontend/widgets/OBSBasic_Scenes.cpp
Normal file
File diff suppressed because it is too large
Load Diff
347
frontend/widgets/OBSBasic_Screenshots.cpp
Normal file
347
frontend/widgets/OBSBasic_Screenshots.cpp
Normal file
@ -0,0 +1,347 @@
|
|||||||
|
/******************************************************************************
|
||||||
|
Copyright (C) 2023 by Lain Bailey <lain@obsproject.com>
|
||||||
|
|
||||||
|
This program is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 2 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
******************************************************************************/
|
||||||
|
|
||||||
|
#include "window-basic-main.hpp"
|
||||||
|
#include "screenshot-obj.hpp"
|
||||||
|
|
||||||
|
#include <qt-wrappers.hpp>
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
#include <wincodec.h>
|
||||||
|
#include <wincodecsdk.h>
|
||||||
|
#include <wrl/client.h>
|
||||||
|
#pragma comment(lib, "windowscodecs.lib")
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static void ScreenshotTick(void *param, float);
|
||||||
|
|
||||||
|
/* ========================================================================= */
|
||||||
|
|
||||||
|
ScreenshotObj::ScreenshotObj(obs_source_t *source) : weakSource(OBSGetWeakRef(source))
|
||||||
|
{
|
||||||
|
obs_add_tick_callback(ScreenshotTick, this);
|
||||||
|
}
|
||||||
|
|
||||||
|
ScreenshotObj::~ScreenshotObj()
|
||||||
|
{
|
||||||
|
obs_enter_graphics();
|
||||||
|
gs_stagesurface_destroy(stagesurf);
|
||||||
|
gs_texrender_destroy(texrender);
|
||||||
|
obs_leave_graphics();
|
||||||
|
|
||||||
|
obs_remove_tick_callback(ScreenshotTick, this);
|
||||||
|
|
||||||
|
if (th.joinable()) {
|
||||||
|
th.join();
|
||||||
|
|
||||||
|
if (cx && cy) {
|
||||||
|
OBSBasic *main = OBSBasic::Get();
|
||||||
|
main->ShowStatusBarMessage(
|
||||||
|
QTStr("Basic.StatusBar.ScreenshotSavedTo").arg(QT_UTF8(path.c_str())));
|
||||||
|
|
||||||
|
main->lastScreenshot = path;
|
||||||
|
|
||||||
|
main->OnEvent(OBS_FRONTEND_EVENT_SCREENSHOT_TAKEN);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ScreenshotObj::Screenshot()
|
||||||
|
{
|
||||||
|
OBSSource source = OBSGetStrongRef(weakSource);
|
||||||
|
|
||||||
|
if (source) {
|
||||||
|
cx = obs_source_get_width(source);
|
||||||
|
cy = obs_source_get_height(source);
|
||||||
|
} else {
|
||||||
|
obs_video_info ovi;
|
||||||
|
obs_get_video_info(&ovi);
|
||||||
|
cx = ovi.base_width;
|
||||||
|
cy = ovi.base_height;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!cx || !cy) {
|
||||||
|
blog(LOG_WARNING, "Cannot screenshot, invalid target size");
|
||||||
|
obs_remove_tick_callback(ScreenshotTick, this);
|
||||||
|
deleteLater();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
enum gs_color_space space = obs_source_get_color_space(source, 0, nullptr);
|
||||||
|
if (space == GS_CS_709_EXTENDED) {
|
||||||
|
/* Convert for JXR */
|
||||||
|
space = GS_CS_709_SCRGB;
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
/* Tonemap to SDR if HDR */
|
||||||
|
const enum gs_color_space space = GS_CS_SRGB;
|
||||||
|
#endif
|
||||||
|
const enum gs_color_format format = gs_get_format_from_space(space);
|
||||||
|
|
||||||
|
texrender = gs_texrender_create(format, GS_ZS_NONE);
|
||||||
|
stagesurf = gs_stagesurface_create(cx, cy, format);
|
||||||
|
|
||||||
|
if (gs_texrender_begin_with_color_space(texrender, cx, cy, space)) {
|
||||||
|
vec4 zero;
|
||||||
|
vec4_zero(&zero);
|
||||||
|
|
||||||
|
gs_clear(GS_CLEAR_COLOR, &zero, 0.0f, 0);
|
||||||
|
gs_ortho(0.0f, (float)cx, 0.0f, (float)cy, -100.0f, 100.0f);
|
||||||
|
|
||||||
|
gs_blend_state_push();
|
||||||
|
gs_blend_function(GS_BLEND_ONE, GS_BLEND_ZERO);
|
||||||
|
|
||||||
|
if (source) {
|
||||||
|
obs_source_inc_showing(source);
|
||||||
|
obs_source_video_render(source);
|
||||||
|
obs_source_dec_showing(source);
|
||||||
|
} else {
|
||||||
|
obs_render_main_texture();
|
||||||
|
}
|
||||||
|
|
||||||
|
gs_blend_state_pop();
|
||||||
|
gs_texrender_end(texrender);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ScreenshotObj::Download()
|
||||||
|
{
|
||||||
|
gs_stage_texture(stagesurf, gs_texrender_get_texture(texrender));
|
||||||
|
}
|
||||||
|
|
||||||
|
void ScreenshotObj::Copy()
|
||||||
|
{
|
||||||
|
uint8_t *videoData = nullptr;
|
||||||
|
uint32_t videoLinesize = 0;
|
||||||
|
|
||||||
|
if (gs_stagesurface_map(stagesurf, &videoData, &videoLinesize)) {
|
||||||
|
if (gs_stagesurface_get_color_format(stagesurf) == GS_RGBA16F) {
|
||||||
|
const uint32_t linesize = cx * 8;
|
||||||
|
half_bytes.reserve(cx * cy * 8);
|
||||||
|
|
||||||
|
for (uint32_t y = 0; y < cy; y++) {
|
||||||
|
const uint8_t *const line = videoData + (y * videoLinesize);
|
||||||
|
half_bytes.insert(half_bytes.end(), line, line + linesize);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
image = QImage(cx, cy, QImage::Format::Format_RGBX8888);
|
||||||
|
|
||||||
|
int linesize = image.bytesPerLine();
|
||||||
|
for (int y = 0; y < (int)cy; y++)
|
||||||
|
memcpy(image.scanLine(y), videoData + (y * videoLinesize), linesize);
|
||||||
|
}
|
||||||
|
|
||||||
|
gs_stagesurface_unmap(stagesurf);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ScreenshotObj::Save()
|
||||||
|
{
|
||||||
|
OBSBasic *main = OBSBasic::Get();
|
||||||
|
config_t *config = main->Config();
|
||||||
|
|
||||||
|
const char *mode = config_get_string(config, "Output", "Mode");
|
||||||
|
const char *type = config_get_string(config, "AdvOut", "RecType");
|
||||||
|
const char *adv_path = strcmp(type, "Standard") ? config_get_string(config, "AdvOut", "FFFilePath")
|
||||||
|
: config_get_string(config, "AdvOut", "RecFilePath");
|
||||||
|
const char *rec_path = strcmp(mode, "Advanced") ? config_get_string(config, "SimpleOutput", "FilePath")
|
||||||
|
: adv_path;
|
||||||
|
|
||||||
|
bool noSpace = config_get_bool(config, "SimpleOutput", "FileNameWithoutSpace");
|
||||||
|
const char *filenameFormat = config_get_string(config, "Output", "FilenameFormatting");
|
||||||
|
bool overwriteIfExists = config_get_bool(config, "Output", "OverwriteIfExists");
|
||||||
|
|
||||||
|
const char *ext = half_bytes.empty() ? "png" : "jxr";
|
||||||
|
path = GetOutputFilename(rec_path, ext, noSpace, overwriteIfExists,
|
||||||
|
GetFormatString(filenameFormat, "Screenshot", nullptr).c_str());
|
||||||
|
|
||||||
|
th = std::thread([this] { MuxAndFinish(); });
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
static HRESULT SaveJxrImage(LPCWSTR path, uint8_t *pixels, uint32_t cx, uint32_t cy, IWICBitmapFrameEncode *frameEncode,
|
||||||
|
IPropertyBag2 *options)
|
||||||
|
{
|
||||||
|
wchar_t lossless[] = L"Lossless";
|
||||||
|
PROPBAG2 bag = {};
|
||||||
|
bag.pstrName = lossless;
|
||||||
|
VARIANT value = {};
|
||||||
|
value.vt = VT_BOOL;
|
||||||
|
value.bVal = TRUE;
|
||||||
|
HRESULT hr = options->Write(1, &bag, &value);
|
||||||
|
if (FAILED(hr))
|
||||||
|
return hr;
|
||||||
|
|
||||||
|
hr = frameEncode->Initialize(options);
|
||||||
|
if (FAILED(hr))
|
||||||
|
return hr;
|
||||||
|
|
||||||
|
hr = frameEncode->SetSize(cx, cy);
|
||||||
|
if (FAILED(hr))
|
||||||
|
return hr;
|
||||||
|
|
||||||
|
hr = frameEncode->SetResolution(72, 72);
|
||||||
|
if (FAILED(hr))
|
||||||
|
return hr;
|
||||||
|
|
||||||
|
WICPixelFormatGUID pixelFormat = GUID_WICPixelFormat64bppRGBAHalf;
|
||||||
|
hr = frameEncode->SetPixelFormat(&pixelFormat);
|
||||||
|
if (FAILED(hr))
|
||||||
|
return hr;
|
||||||
|
|
||||||
|
if (memcmp(&pixelFormat, &GUID_WICPixelFormat64bppRGBAHalf, sizeof(WICPixelFormatGUID)) != 0)
|
||||||
|
return E_FAIL;
|
||||||
|
|
||||||
|
hr = frameEncode->WritePixels(cy, cx * 8, cx * cy * 8, pixels);
|
||||||
|
if (FAILED(hr))
|
||||||
|
return hr;
|
||||||
|
|
||||||
|
hr = frameEncode->Commit();
|
||||||
|
if (FAILED(hr))
|
||||||
|
return hr;
|
||||||
|
|
||||||
|
return S_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static HRESULT SaveJxr(LPCWSTR path, uint8_t *pixels, uint32_t cx, uint32_t cy)
|
||||||
|
{
|
||||||
|
Microsoft::WRL::ComPtr<IWICImagingFactory> factory;
|
||||||
|
HRESULT hr = CoCreateInstance(CLSID_WICImagingFactory, NULL, CLSCTX_INPROC_SERVER,
|
||||||
|
IID_PPV_ARGS(factory.GetAddressOf()));
|
||||||
|
if (FAILED(hr))
|
||||||
|
return hr;
|
||||||
|
|
||||||
|
Microsoft::WRL::ComPtr<IWICStream> stream;
|
||||||
|
hr = factory->CreateStream(stream.GetAddressOf());
|
||||||
|
if (FAILED(hr))
|
||||||
|
return hr;
|
||||||
|
|
||||||
|
hr = stream->InitializeFromFilename(path, GENERIC_WRITE);
|
||||||
|
if (FAILED(hr))
|
||||||
|
return hr;
|
||||||
|
|
||||||
|
Microsoft::WRL::ComPtr<IWICBitmapEncoder> encoder = NULL;
|
||||||
|
hr = factory->CreateEncoder(GUID_ContainerFormatWmp, NULL, encoder.GetAddressOf());
|
||||||
|
if (FAILED(hr))
|
||||||
|
return hr;
|
||||||
|
|
||||||
|
hr = encoder->Initialize(stream.Get(), WICBitmapEncoderNoCache);
|
||||||
|
if (FAILED(hr))
|
||||||
|
return hr;
|
||||||
|
|
||||||
|
Microsoft::WRL::ComPtr<IWICBitmapFrameEncode> frameEncode;
|
||||||
|
Microsoft::WRL::ComPtr<IPropertyBag2> options;
|
||||||
|
hr = encoder->CreateNewFrame(frameEncode.GetAddressOf(), options.GetAddressOf());
|
||||||
|
if (FAILED(hr))
|
||||||
|
return hr;
|
||||||
|
|
||||||
|
hr = SaveJxrImage(path, pixels, cx, cy, frameEncode.Get(), options.Get());
|
||||||
|
if (FAILED(hr))
|
||||||
|
return hr;
|
||||||
|
|
||||||
|
encoder->Commit();
|
||||||
|
return S_OK;
|
||||||
|
}
|
||||||
|
#endif // #ifdef _WIN32
|
||||||
|
|
||||||
|
void ScreenshotObj::MuxAndFinish()
|
||||||
|
{
|
||||||
|
if (half_bytes.empty()) {
|
||||||
|
image.save(QT_UTF8(path.c_str()));
|
||||||
|
blog(LOG_INFO, "Saved screenshot to '%s'", path.c_str());
|
||||||
|
} else {
|
||||||
|
#ifdef _WIN32
|
||||||
|
wchar_t *path_w = nullptr;
|
||||||
|
os_utf8_to_wcs_ptr(path.c_str(), 0, &path_w);
|
||||||
|
if (path_w) {
|
||||||
|
SaveJxr(path_w, half_bytes.data(), cx, cy);
|
||||||
|
bfree(path_w);
|
||||||
|
}
|
||||||
|
#endif // #ifdef _WIN32
|
||||||
|
}
|
||||||
|
|
||||||
|
deleteLater();
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ========================================================================= */
|
||||||
|
|
||||||
|
#define STAGE_SCREENSHOT 0
|
||||||
|
#define STAGE_DOWNLOAD 1
|
||||||
|
#define STAGE_COPY_AND_SAVE 2
|
||||||
|
#define STAGE_FINISH 3
|
||||||
|
|
||||||
|
static void ScreenshotTick(void *param, float)
|
||||||
|
{
|
||||||
|
ScreenshotObj *data = reinterpret_cast<ScreenshotObj *>(param);
|
||||||
|
|
||||||
|
if (data->stage == STAGE_FINISH) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
obs_enter_graphics();
|
||||||
|
|
||||||
|
switch (data->stage) {
|
||||||
|
case STAGE_SCREENSHOT:
|
||||||
|
data->Screenshot();
|
||||||
|
break;
|
||||||
|
case STAGE_DOWNLOAD:
|
||||||
|
data->Download();
|
||||||
|
break;
|
||||||
|
case STAGE_COPY_AND_SAVE:
|
||||||
|
data->Copy();
|
||||||
|
QMetaObject::invokeMethod(data, "Save");
|
||||||
|
obs_remove_tick_callback(ScreenshotTick, data);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
obs_leave_graphics();
|
||||||
|
|
||||||
|
data->stage++;
|
||||||
|
}
|
||||||
|
|
||||||
|
void OBSBasic::Screenshot(OBSSource source)
|
||||||
|
{
|
||||||
|
if (!!screenshotData) {
|
||||||
|
blog(LOG_WARNING, "Cannot take new screenshot, "
|
||||||
|
"screenshot currently in progress");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
screenshotData = new ScreenshotObj(source);
|
||||||
|
}
|
||||||
|
|
||||||
|
void OBSBasic::ScreenshotSelectedSource()
|
||||||
|
{
|
||||||
|
OBSSceneItem item = GetCurrentSceneItem();
|
||||||
|
if (item) {
|
||||||
|
Screenshot(obs_sceneitem_get_source(item));
|
||||||
|
} else {
|
||||||
|
blog(LOG_INFO, "Could not take a source screenshot: "
|
||||||
|
"no source selected");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void OBSBasic::ScreenshotProgram()
|
||||||
|
{
|
||||||
|
Screenshot(GetProgramSource());
|
||||||
|
}
|
||||||
|
|
||||||
|
void OBSBasic::ScreenshotScene()
|
||||||
|
{
|
||||||
|
Screenshot(GetCurrentSceneSource());
|
||||||
|
}
|
10203
frontend/widgets/OBSBasic_Service.cpp
Normal file
10203
frontend/widgets/OBSBasic_Service.cpp
Normal file
File diff suppressed because it is too large
Load Diff
10203
frontend/widgets/OBSBasic_StatusBar.cpp
Normal file
10203
frontend/widgets/OBSBasic_StatusBar.cpp
Normal file
File diff suppressed because it is too large
Load Diff
10203
frontend/widgets/OBSBasic_Streaming.cpp
Normal file
10203
frontend/widgets/OBSBasic_Streaming.cpp
Normal file
File diff suppressed because it is too large
Load Diff
10203
frontend/widgets/OBSBasic_StudioMode.cpp
Normal file
10203
frontend/widgets/OBSBasic_StudioMode.cpp
Normal file
File diff suppressed because it is too large
Load Diff
10203
frontend/widgets/OBSBasic_SysTray.cpp
Normal file
10203
frontend/widgets/OBSBasic_SysTray.cpp
Normal file
File diff suppressed because it is too large
Load Diff
10203
frontend/widgets/OBSBasic_Transitions.cpp
Normal file
10203
frontend/widgets/OBSBasic_Transitions.cpp
Normal file
File diff suppressed because it is too large
Load Diff
10203
frontend/widgets/OBSBasic_Updater.cpp
Normal file
10203
frontend/widgets/OBSBasic_Updater.cpp
Normal file
File diff suppressed because it is too large
Load Diff
10203
frontend/widgets/OBSBasic_VirtualCam.cpp
Normal file
10203
frontend/widgets/OBSBasic_VirtualCam.cpp
Normal file
File diff suppressed because it is too large
Load Diff
10203
frontend/widgets/OBSBasic_VolControl.cpp
Normal file
10203
frontend/widgets/OBSBasic_VolControl.cpp
Normal file
File diff suppressed because it is too large
Load Diff
10203
frontend/widgets/OBSBasic_YouTube.cpp
Normal file
10203
frontend/widgets/OBSBasic_YouTube.cpp
Normal file
File diff suppressed because it is too large
Load Diff
243
frontend/widgets/OBSQTDisplay.cpp
Normal file
243
frontend/widgets/OBSQTDisplay.cpp
Normal file
@ -0,0 +1,243 @@
|
|||||||
|
#include "moc_qt-display.cpp"
|
||||||
|
#include "display-helpers.hpp"
|
||||||
|
#include <QWindow>
|
||||||
|
#include <QScreen>
|
||||||
|
#include <QResizeEvent>
|
||||||
|
#include <QShowEvent>
|
||||||
|
|
||||||
|
#include <qt-wrappers.hpp>
|
||||||
|
#include <obs-config.h>
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
#define WIN32_LEAN_AND_MEAN
|
||||||
|
#include <Windows.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if !defined(_WIN32) && !defined(__APPLE__)
|
||||||
|
#include <obs-nix-platform.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef ENABLE_WAYLAND
|
||||||
|
#include <qpa/qplatformnativeinterface.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
class SurfaceEventFilter : public QObject {
|
||||||
|
OBSQTDisplay *display;
|
||||||
|
|
||||||
|
public:
|
||||||
|
SurfaceEventFilter(OBSQTDisplay *src) : QObject(src), display(src) {}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
bool eventFilter(QObject *obj, QEvent *event) override
|
||||||
|
{
|
||||||
|
bool result = QObject::eventFilter(obj, event);
|
||||||
|
QPlatformSurfaceEvent *surfaceEvent;
|
||||||
|
|
||||||
|
switch (event->type()) {
|
||||||
|
case QEvent::PlatformSurface:
|
||||||
|
surfaceEvent = static_cast<QPlatformSurfaceEvent *>(event);
|
||||||
|
|
||||||
|
switch (surfaceEvent->surfaceEventType()) {
|
||||||
|
case QPlatformSurfaceEvent::SurfaceAboutToBeDestroyed:
|
||||||
|
display->DestroyDisplay();
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
static inline long long color_to_int(const QColor &color)
|
||||||
|
{
|
||||||
|
auto shift = [&](unsigned val, int shift) {
|
||||||
|
return ((val & 0xff) << shift);
|
||||||
|
};
|
||||||
|
|
||||||
|
return shift(color.red(), 0) | shift(color.green(), 8) | shift(color.blue(), 16) | shift(color.alpha(), 24);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline QColor rgba_to_color(uint32_t rgba)
|
||||||
|
{
|
||||||
|
return QColor::fromRgb(rgba & 0xFF, (rgba >> 8) & 0xFF, (rgba >> 16) & 0xFF, (rgba >> 24) & 0xFF);
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool QTToGSWindow(QWindow *window, gs_window &gswindow)
|
||||||
|
{
|
||||||
|
bool success = true;
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
gswindow.hwnd = (HWND)window->winId();
|
||||||
|
#elif __APPLE__
|
||||||
|
gswindow.view = (id)window->winId();
|
||||||
|
#else
|
||||||
|
switch (obs_get_nix_platform()) {
|
||||||
|
case OBS_NIX_PLATFORM_X11_EGL:
|
||||||
|
gswindow.id = window->winId();
|
||||||
|
gswindow.display = obs_get_nix_platform_display();
|
||||||
|
break;
|
||||||
|
#ifdef ENABLE_WAYLAND
|
||||||
|
case OBS_NIX_PLATFORM_WAYLAND: {
|
||||||
|
QPlatformNativeInterface *native = QGuiApplication::platformNativeInterface();
|
||||||
|
gswindow.display = native->nativeResourceForWindow("surface", window);
|
||||||
|
success = gswindow.display != nullptr;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
default:
|
||||||
|
success = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
return success;
|
||||||
|
}
|
||||||
|
|
||||||
|
OBSQTDisplay::OBSQTDisplay(QWidget *parent, Qt::WindowFlags flags) : QWidget(parent, flags)
|
||||||
|
{
|
||||||
|
setAttribute(Qt::WA_PaintOnScreen);
|
||||||
|
setAttribute(Qt::WA_StaticContents);
|
||||||
|
setAttribute(Qt::WA_NoSystemBackground);
|
||||||
|
setAttribute(Qt::WA_OpaquePaintEvent);
|
||||||
|
setAttribute(Qt::WA_DontCreateNativeAncestors);
|
||||||
|
setAttribute(Qt::WA_NativeWindow);
|
||||||
|
|
||||||
|
auto windowVisible = [this](bool visible) {
|
||||||
|
if (!visible) {
|
||||||
|
#if !defined(_WIN32) && !defined(__APPLE__)
|
||||||
|
display = nullptr;
|
||||||
|
#endif
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!display) {
|
||||||
|
CreateDisplay();
|
||||||
|
} else {
|
||||||
|
QSize size = GetPixelSize(this);
|
||||||
|
obs_display_resize(display, size.width(), size.height());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
auto screenChanged = [this](QScreen *) {
|
||||||
|
CreateDisplay();
|
||||||
|
|
||||||
|
QSize size = GetPixelSize(this);
|
||||||
|
obs_display_resize(display, size.width(), size.height());
|
||||||
|
};
|
||||||
|
|
||||||
|
connect(windowHandle(), &QWindow::visibleChanged, windowVisible);
|
||||||
|
connect(windowHandle(), &QWindow::screenChanged, screenChanged);
|
||||||
|
|
||||||
|
windowHandle()->installEventFilter(new SurfaceEventFilter(this));
|
||||||
|
}
|
||||||
|
|
||||||
|
QColor OBSQTDisplay::GetDisplayBackgroundColor() const
|
||||||
|
{
|
||||||
|
return rgba_to_color(backgroundColor);
|
||||||
|
}
|
||||||
|
|
||||||
|
void OBSQTDisplay::SetDisplayBackgroundColor(const QColor &color)
|
||||||
|
{
|
||||||
|
uint32_t newBackgroundColor = (uint32_t)color_to_int(color);
|
||||||
|
|
||||||
|
if (newBackgroundColor != backgroundColor) {
|
||||||
|
backgroundColor = newBackgroundColor;
|
||||||
|
UpdateDisplayBackgroundColor();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void OBSQTDisplay::UpdateDisplayBackgroundColor()
|
||||||
|
{
|
||||||
|
obs_display_set_background_color(display, backgroundColor);
|
||||||
|
}
|
||||||
|
|
||||||
|
void OBSQTDisplay::CreateDisplay()
|
||||||
|
{
|
||||||
|
if (display)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (destroying)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (!windowHandle()->isExposed())
|
||||||
|
return;
|
||||||
|
|
||||||
|
QSize size = GetPixelSize(this);
|
||||||
|
|
||||||
|
gs_init_data info = {};
|
||||||
|
info.cx = size.width();
|
||||||
|
info.cy = size.height();
|
||||||
|
info.format = GS_BGRA;
|
||||||
|
info.zsformat = GS_ZS_NONE;
|
||||||
|
|
||||||
|
if (!QTToGSWindow(windowHandle(), info.window))
|
||||||
|
return;
|
||||||
|
|
||||||
|
display = obs_display_create(&info, backgroundColor);
|
||||||
|
|
||||||
|
emit DisplayCreated(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
void OBSQTDisplay::paintEvent(QPaintEvent *event)
|
||||||
|
{
|
||||||
|
CreateDisplay();
|
||||||
|
|
||||||
|
QWidget::paintEvent(event);
|
||||||
|
}
|
||||||
|
|
||||||
|
void OBSQTDisplay::moveEvent(QMoveEvent *event)
|
||||||
|
{
|
||||||
|
QWidget::moveEvent(event);
|
||||||
|
|
||||||
|
OnMove();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool OBSQTDisplay::nativeEvent(const QByteArray &, void *message, qintptr *)
|
||||||
|
{
|
||||||
|
#ifdef _WIN32
|
||||||
|
const MSG &msg = *static_cast<MSG *>(message);
|
||||||
|
switch (msg.message) {
|
||||||
|
case WM_DISPLAYCHANGE:
|
||||||
|
OnDisplayChange();
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
UNUSED_PARAMETER(message);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void OBSQTDisplay::resizeEvent(QResizeEvent *event)
|
||||||
|
{
|
||||||
|
QWidget::resizeEvent(event);
|
||||||
|
|
||||||
|
CreateDisplay();
|
||||||
|
|
||||||
|
if (isVisible() && display) {
|
||||||
|
QSize size = GetPixelSize(this);
|
||||||
|
obs_display_resize(display, size.width(), size.height());
|
||||||
|
}
|
||||||
|
|
||||||
|
emit DisplayResized();
|
||||||
|
}
|
||||||
|
|
||||||
|
QPaintEngine *OBSQTDisplay::paintEngine() const
|
||||||
|
{
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
void OBSQTDisplay::OnMove()
|
||||||
|
{
|
||||||
|
if (display)
|
||||||
|
obs_display_update_color_space(display);
|
||||||
|
}
|
||||||
|
|
||||||
|
void OBSQTDisplay::OnDisplayChange()
|
||||||
|
{
|
||||||
|
if (display)
|
||||||
|
obs_display_update_color_space(display);
|
||||||
|
}
|
601
frontend/widgets/StatusBarWidget.cpp
Normal file
601
frontend/widgets/StatusBarWidget.cpp
Normal file
@ -0,0 +1,601 @@
|
|||||||
|
#include <QPainter>
|
||||||
|
#include <QPixmap>
|
||||||
|
#include "obs-app.hpp"
|
||||||
|
#include "window-basic-main.hpp"
|
||||||
|
#include "moc_window-basic-status-bar.cpp"
|
||||||
|
#include "window-basic-main-outputs.hpp"
|
||||||
|
#include "qt-wrappers.hpp"
|
||||||
|
#include "platform.hpp"
|
||||||
|
|
||||||
|
#include "ui_StatusBarWidget.h"
|
||||||
|
|
||||||
|
static constexpr int bitrateUpdateSeconds = 2;
|
||||||
|
static constexpr int congestionUpdateSeconds = 4;
|
||||||
|
static constexpr float excellentThreshold = 0.0f;
|
||||||
|
static constexpr float goodThreshold = 0.3333f;
|
||||||
|
static constexpr float mediocreThreshold = 0.6667f;
|
||||||
|
static constexpr float badThreshold = 1.0f;
|
||||||
|
|
||||||
|
StatusBarWidget::StatusBarWidget(QWidget *parent) : QWidget(parent), ui(new Ui::StatusBarWidget)
|
||||||
|
{
|
||||||
|
ui->setupUi(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
StatusBarWidget::~StatusBarWidget() {}
|
||||||
|
|
||||||
|
OBSBasicStatusBar::OBSBasicStatusBar(QWidget *parent)
|
||||||
|
: QStatusBar(parent),
|
||||||
|
excellentPixmap(QIcon(":/res/images/network-excellent.svg").pixmap(QSize(16, 16))),
|
||||||
|
goodPixmap(QIcon(":/res/images/network-good.svg").pixmap(QSize(16, 16))),
|
||||||
|
mediocrePixmap(QIcon(":/res/images/network-mediocre.svg").pixmap(QSize(16, 16))),
|
||||||
|
badPixmap(QIcon(":/res/images/network-bad.svg").pixmap(QSize(16, 16))),
|
||||||
|
recordingActivePixmap(QIcon(":/res/images/recording-active.svg").pixmap(QSize(16, 16))),
|
||||||
|
recordingPausePixmap(QIcon(":/res/images/recording-pause.svg").pixmap(QSize(16, 16))),
|
||||||
|
streamingActivePixmap(QIcon(":/res/images/streaming-active.svg").pixmap(QSize(16, 16)))
|
||||||
|
{
|
||||||
|
congestionArray.reserve(congestionUpdateSeconds);
|
||||||
|
|
||||||
|
statusWidget = new StatusBarWidget(this);
|
||||||
|
statusWidget->ui->delayInfo->setText("");
|
||||||
|
statusWidget->ui->droppedFrames->setText(QTStr("DroppedFrames").arg("0", "0.0"));
|
||||||
|
statusWidget->ui->statusIcon->setPixmap(inactivePixmap);
|
||||||
|
statusWidget->ui->streamIcon->setPixmap(streamingInactivePixmap);
|
||||||
|
statusWidget->ui->streamTime->setDisabled(true);
|
||||||
|
statusWidget->ui->recordIcon->setPixmap(recordingInactivePixmap);
|
||||||
|
statusWidget->ui->recordTime->setDisabled(true);
|
||||||
|
statusWidget->ui->delayFrame->hide();
|
||||||
|
statusWidget->ui->issuesFrame->hide();
|
||||||
|
statusWidget->ui->kbps->hide();
|
||||||
|
|
||||||
|
addPermanentWidget(statusWidget, 1);
|
||||||
|
setMinimumHeight(statusWidget->height());
|
||||||
|
|
||||||
|
UpdateIcons();
|
||||||
|
connect(App(), &OBSApp::StyleChanged, this, &OBSBasicStatusBar::UpdateIcons);
|
||||||
|
|
||||||
|
messageTimer = new QTimer(this);
|
||||||
|
messageTimer->setSingleShot(true);
|
||||||
|
connect(messageTimer, &QTimer::timeout, this, &OBSBasicStatusBar::clearMessage);
|
||||||
|
|
||||||
|
clearMessage();
|
||||||
|
}
|
||||||
|
|
||||||
|
void OBSBasicStatusBar::Activate()
|
||||||
|
{
|
||||||
|
if (!active) {
|
||||||
|
refreshTimer = new QTimer(this);
|
||||||
|
connect(refreshTimer, &QTimer::timeout, this, &OBSBasicStatusBar::UpdateStatusBar);
|
||||||
|
|
||||||
|
int skipped = video_output_get_skipped_frames(obs_get_video());
|
||||||
|
int total = video_output_get_total_frames(obs_get_video());
|
||||||
|
|
||||||
|
totalStreamSeconds = 0;
|
||||||
|
totalRecordSeconds = 0;
|
||||||
|
lastSkippedFrameCount = 0;
|
||||||
|
startSkippedFrameCount = skipped;
|
||||||
|
startTotalFrameCount = total;
|
||||||
|
|
||||||
|
refreshTimer->start(1000);
|
||||||
|
active = true;
|
||||||
|
|
||||||
|
if (streamOutput) {
|
||||||
|
statusWidget->ui->statusIcon->setPixmap(inactivePixmap);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (streamOutput) {
|
||||||
|
statusWidget->ui->streamIcon->setPixmap(streamingActivePixmap);
|
||||||
|
statusWidget->ui->streamTime->setDisabled(false);
|
||||||
|
statusWidget->ui->issuesFrame->show();
|
||||||
|
statusWidget->ui->kbps->show();
|
||||||
|
firstCongestionUpdate = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (recordOutput) {
|
||||||
|
statusWidget->ui->recordIcon->setPixmap(recordingActivePixmap);
|
||||||
|
statusWidget->ui->recordTime->setDisabled(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void OBSBasicStatusBar::Deactivate()
|
||||||
|
{
|
||||||
|
OBSBasic *main = qobject_cast<OBSBasic *>(parent());
|
||||||
|
if (!main)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (!streamOutput) {
|
||||||
|
statusWidget->ui->streamTime->setText(QString("00:00:00"));
|
||||||
|
statusWidget->ui->streamTime->setDisabled(true);
|
||||||
|
statusWidget->ui->streamIcon->setPixmap(streamingInactivePixmap);
|
||||||
|
statusWidget->ui->statusIcon->setPixmap(inactivePixmap);
|
||||||
|
statusWidget->ui->delayFrame->hide();
|
||||||
|
statusWidget->ui->issuesFrame->hide();
|
||||||
|
statusWidget->ui->kbps->hide();
|
||||||
|
totalStreamSeconds = 0;
|
||||||
|
congestionArray.clear();
|
||||||
|
disconnected = false;
|
||||||
|
firstCongestionUpdate = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!recordOutput) {
|
||||||
|
statusWidget->ui->recordTime->setText(QString("00:00:00"));
|
||||||
|
statusWidget->ui->recordTime->setDisabled(true);
|
||||||
|
statusWidget->ui->recordIcon->setPixmap(recordingInactivePixmap);
|
||||||
|
totalRecordSeconds = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (main->outputHandler && !main->outputHandler->Active()) {
|
||||||
|
delete refreshTimer;
|
||||||
|
|
||||||
|
statusWidget->ui->delayInfo->setText("");
|
||||||
|
statusWidget->ui->droppedFrames->setText(QTStr("DroppedFrames").arg("0", "0.0"));
|
||||||
|
statusWidget->ui->kbps->setText("0 kbps");
|
||||||
|
|
||||||
|
delaySecTotal = 0;
|
||||||
|
delaySecStarting = 0;
|
||||||
|
delaySecStopping = 0;
|
||||||
|
reconnectTimeout = 0;
|
||||||
|
active = false;
|
||||||
|
overloadedNotify = true;
|
||||||
|
|
||||||
|
statusWidget->ui->statusIcon->setPixmap(inactivePixmap);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void OBSBasicStatusBar::UpdateDelayMsg()
|
||||||
|
{
|
||||||
|
QString msg;
|
||||||
|
|
||||||
|
if (delaySecTotal) {
|
||||||
|
if (delaySecStarting && !delaySecStopping) {
|
||||||
|
msg = QTStr("Basic.StatusBar.DelayStartingIn");
|
||||||
|
msg = msg.arg(QString::number(delaySecStarting));
|
||||||
|
|
||||||
|
} else if (!delaySecStarting && delaySecStopping) {
|
||||||
|
msg = QTStr("Basic.StatusBar.DelayStoppingIn");
|
||||||
|
msg = msg.arg(QString::number(delaySecStopping));
|
||||||
|
|
||||||
|
} else if (delaySecStarting && delaySecStopping) {
|
||||||
|
msg = QTStr("Basic.StatusBar.DelayStartingStoppingIn");
|
||||||
|
msg = msg.arg(QString::number(delaySecStopping), QString::number(delaySecStarting));
|
||||||
|
} else {
|
||||||
|
msg = QTStr("Basic.StatusBar.Delay");
|
||||||
|
msg = msg.arg(QString::number(delaySecTotal));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!statusWidget->ui->delayFrame->isVisible())
|
||||||
|
statusWidget->ui->delayFrame->show();
|
||||||
|
|
||||||
|
statusWidget->ui->delayInfo->setText(msg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void OBSBasicStatusBar::UpdateBandwidth()
|
||||||
|
{
|
||||||
|
if (!streamOutput)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (++seconds < bitrateUpdateSeconds)
|
||||||
|
return;
|
||||||
|
|
||||||
|
OBSOutput output = OBSGetStrongRef(streamOutput);
|
||||||
|
if (!output)
|
||||||
|
return;
|
||||||
|
|
||||||
|
uint64_t bytesSent = obs_output_get_total_bytes(output);
|
||||||
|
uint64_t bytesSentTime = os_gettime_ns();
|
||||||
|
|
||||||
|
if (bytesSent < lastBytesSent)
|
||||||
|
bytesSent = 0;
|
||||||
|
if (bytesSent == 0)
|
||||||
|
lastBytesSent = 0;
|
||||||
|
|
||||||
|
uint64_t bitsBetween = (bytesSent - lastBytesSent) * 8;
|
||||||
|
|
||||||
|
double timePassed = double(bytesSentTime - lastBytesSentTime) / 1000000000.0;
|
||||||
|
|
||||||
|
double kbitsPerSec = double(bitsBetween) / timePassed / 1000.0;
|
||||||
|
|
||||||
|
QString text;
|
||||||
|
text += QString::number(kbitsPerSec, 'f', 0) + QString(" kbps");
|
||||||
|
|
||||||
|
statusWidget->ui->kbps->setText(text);
|
||||||
|
statusWidget->ui->kbps->setMinimumWidth(statusWidget->ui->kbps->width());
|
||||||
|
|
||||||
|
if (!statusWidget->ui->kbps->isVisible())
|
||||||
|
statusWidget->ui->kbps->show();
|
||||||
|
|
||||||
|
lastBytesSent = bytesSent;
|
||||||
|
lastBytesSentTime = bytesSentTime;
|
||||||
|
seconds = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void OBSBasicStatusBar::UpdateCPUUsage()
|
||||||
|
{
|
||||||
|
OBSBasic *main = qobject_cast<OBSBasic *>(parent());
|
||||||
|
if (!main)
|
||||||
|
return;
|
||||||
|
|
||||||
|
QString text;
|
||||||
|
text += QString("CPU: ") + QString::number(main->GetCPUUsage(), 'f', 1) + QString("%");
|
||||||
|
|
||||||
|
statusWidget->ui->cpuUsage->setText(text);
|
||||||
|
statusWidget->ui->cpuUsage->setMinimumWidth(statusWidget->ui->cpuUsage->width());
|
||||||
|
|
||||||
|
UpdateCurrentFPS();
|
||||||
|
}
|
||||||
|
|
||||||
|
void OBSBasicStatusBar::UpdateCurrentFPS()
|
||||||
|
{
|
||||||
|
struct obs_video_info ovi;
|
||||||
|
obs_get_video_info(&ovi);
|
||||||
|
float targetFPS = (float)ovi.fps_num / (float)ovi.fps_den;
|
||||||
|
|
||||||
|
QString text = QString::asprintf("%.2f / %.2f FPS", obs_get_active_fps(), targetFPS);
|
||||||
|
|
||||||
|
statusWidget->ui->fpsCurrent->setText(text);
|
||||||
|
statusWidget->ui->fpsCurrent->setMinimumWidth(statusWidget->ui->fpsCurrent->width());
|
||||||
|
}
|
||||||
|
|
||||||
|
void OBSBasicStatusBar::UpdateStreamTime()
|
||||||
|
{
|
||||||
|
totalStreamSeconds++;
|
||||||
|
|
||||||
|
int seconds = totalStreamSeconds % 60;
|
||||||
|
int totalMinutes = totalStreamSeconds / 60;
|
||||||
|
int minutes = totalMinutes % 60;
|
||||||
|
int hours = totalMinutes / 60;
|
||||||
|
|
||||||
|
QString text = QString::asprintf("%02d:%02d:%02d", hours, minutes, seconds);
|
||||||
|
statusWidget->ui->streamTime->setText(text);
|
||||||
|
if (streamOutput && !statusWidget->ui->streamTime->isEnabled())
|
||||||
|
statusWidget->ui->streamTime->setDisabled(false);
|
||||||
|
|
||||||
|
if (reconnectTimeout > 0) {
|
||||||
|
QString msg = QTStr("Basic.StatusBar.Reconnecting")
|
||||||
|
.arg(QString::number(retries), QString::number(reconnectTimeout));
|
||||||
|
showMessage(msg);
|
||||||
|
disconnected = true;
|
||||||
|
statusWidget->ui->statusIcon->setPixmap(disconnectedPixmap);
|
||||||
|
congestionArray.clear();
|
||||||
|
reconnectTimeout--;
|
||||||
|
|
||||||
|
} else if (retries > 0) {
|
||||||
|
QString msg = QTStr("Basic.StatusBar.AttemptingReconnect");
|
||||||
|
showMessage(msg.arg(QString::number(retries)));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (delaySecStopping > 0 || delaySecStarting > 0) {
|
||||||
|
if (delaySecStopping > 0)
|
||||||
|
--delaySecStopping;
|
||||||
|
if (delaySecStarting > 0)
|
||||||
|
--delaySecStarting;
|
||||||
|
UpdateDelayMsg();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extern volatile bool recording_paused;
|
||||||
|
|
||||||
|
void OBSBasicStatusBar::UpdateRecordTime()
|
||||||
|
{
|
||||||
|
bool paused = os_atomic_load_bool(&recording_paused);
|
||||||
|
|
||||||
|
if (!paused) {
|
||||||
|
totalRecordSeconds++;
|
||||||
|
|
||||||
|
if (recordOutput && !statusWidget->ui->recordTime->isEnabled())
|
||||||
|
statusWidget->ui->recordTime->setDisabled(false);
|
||||||
|
} else {
|
||||||
|
statusWidget->ui->recordIcon->setPixmap(streamPauseIconToggle ? recordingPauseInactivePixmap
|
||||||
|
: recordingPausePixmap);
|
||||||
|
|
||||||
|
streamPauseIconToggle = !streamPauseIconToggle;
|
||||||
|
}
|
||||||
|
|
||||||
|
UpdateRecordTimeLabel();
|
||||||
|
}
|
||||||
|
|
||||||
|
void OBSBasicStatusBar::UpdateRecordTimeLabel()
|
||||||
|
{
|
||||||
|
int seconds = totalRecordSeconds % 60;
|
||||||
|
int totalMinutes = totalRecordSeconds / 60;
|
||||||
|
int minutes = totalMinutes % 60;
|
||||||
|
int hours = totalMinutes / 60;
|
||||||
|
|
||||||
|
QString text = QString::asprintf("%02d:%02d:%02d", hours, minutes, seconds);
|
||||||
|
if (os_atomic_load_bool(&recording_paused)) {
|
||||||
|
text += QStringLiteral(" (PAUSED)");
|
||||||
|
}
|
||||||
|
|
||||||
|
statusWidget->ui->recordTime->setText(text);
|
||||||
|
}
|
||||||
|
|
||||||
|
void OBSBasicStatusBar::UpdateDroppedFrames()
|
||||||
|
{
|
||||||
|
if (!streamOutput)
|
||||||
|
return;
|
||||||
|
|
||||||
|
OBSOutput output = OBSGetStrongRef(streamOutput);
|
||||||
|
if (!output)
|
||||||
|
return;
|
||||||
|
|
||||||
|
int totalDropped = obs_output_get_frames_dropped(output);
|
||||||
|
int totalFrames = obs_output_get_total_frames(output);
|
||||||
|
double percent = (double)totalDropped / (double)totalFrames * 100.0;
|
||||||
|
|
||||||
|
if (!totalFrames)
|
||||||
|
return;
|
||||||
|
|
||||||
|
QString text = QTStr("DroppedFrames");
|
||||||
|
text = text.arg(QString::number(totalDropped), QString::number(percent, 'f', 1));
|
||||||
|
statusWidget->ui->droppedFrames->setText(text);
|
||||||
|
|
||||||
|
if (!statusWidget->ui->issuesFrame->isVisible())
|
||||||
|
statusWidget->ui->issuesFrame->show();
|
||||||
|
|
||||||
|
/* ----------------------------------- *
|
||||||
|
* calculate congestion color */
|
||||||
|
|
||||||
|
float congestion = obs_output_get_congestion(output);
|
||||||
|
float avgCongestion = (congestion + lastCongestion) * 0.5f;
|
||||||
|
if (avgCongestion < congestion)
|
||||||
|
avgCongestion = congestion;
|
||||||
|
if (avgCongestion > 1.0f)
|
||||||
|
avgCongestion = 1.0f;
|
||||||
|
|
||||||
|
lastCongestion = congestion;
|
||||||
|
|
||||||
|
if (disconnected)
|
||||||
|
return;
|
||||||
|
|
||||||
|
bool update = firstCongestionUpdate;
|
||||||
|
float congestionOverTime = avgCongestion;
|
||||||
|
|
||||||
|
if (congestionArray.size() >= congestionUpdateSeconds) {
|
||||||
|
congestionOverTime = accumulate(congestionArray.begin(), congestionArray.end(), 0.0f) /
|
||||||
|
(float)congestionArray.size();
|
||||||
|
congestionArray.clear();
|
||||||
|
update = true;
|
||||||
|
} else {
|
||||||
|
congestionArray.emplace_back(avgCongestion);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (update) {
|
||||||
|
if (congestionOverTime <= excellentThreshold + EPSILON)
|
||||||
|
statusWidget->ui->statusIcon->setPixmap(excellentPixmap);
|
||||||
|
else if (congestionOverTime <= goodThreshold)
|
||||||
|
statusWidget->ui->statusIcon->setPixmap(goodPixmap);
|
||||||
|
else if (congestionOverTime <= mediocreThreshold)
|
||||||
|
statusWidget->ui->statusIcon->setPixmap(mediocrePixmap);
|
||||||
|
else if (congestionOverTime <= badThreshold)
|
||||||
|
statusWidget->ui->statusIcon->setPixmap(badPixmap);
|
||||||
|
|
||||||
|
firstCongestionUpdate = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void OBSBasicStatusBar::OBSOutputReconnect(void *data, calldata_t *params)
|
||||||
|
{
|
||||||
|
OBSBasicStatusBar *statusBar = reinterpret_cast<OBSBasicStatusBar *>(data);
|
||||||
|
|
||||||
|
int seconds = (int)calldata_int(params, "timeout_sec");
|
||||||
|
QMetaObject::invokeMethod(statusBar, "Reconnect", Q_ARG(int, seconds));
|
||||||
|
}
|
||||||
|
|
||||||
|
void OBSBasicStatusBar::OBSOutputReconnectSuccess(void *data, calldata_t *)
|
||||||
|
{
|
||||||
|
OBSBasicStatusBar *statusBar = reinterpret_cast<OBSBasicStatusBar *>(data);
|
||||||
|
|
||||||
|
QMetaObject::invokeMethod(statusBar, "ReconnectSuccess");
|
||||||
|
}
|
||||||
|
|
||||||
|
void OBSBasicStatusBar::Reconnect(int seconds)
|
||||||
|
{
|
||||||
|
OBSBasic *main = qobject_cast<OBSBasic *>(parent());
|
||||||
|
|
||||||
|
if (!retries)
|
||||||
|
main->SysTrayNotify(QTStr("Basic.SystemTray.Message.Reconnecting"), QSystemTrayIcon::Warning);
|
||||||
|
|
||||||
|
reconnectTimeout = seconds;
|
||||||
|
|
||||||
|
if (streamOutput) {
|
||||||
|
OBSOutput output = OBSGetStrongRef(streamOutput);
|
||||||
|
if (!output)
|
||||||
|
return;
|
||||||
|
|
||||||
|
delaySecTotal = obs_output_get_active_delay(output);
|
||||||
|
UpdateDelayMsg();
|
||||||
|
|
||||||
|
retries++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void OBSBasicStatusBar::ReconnectClear()
|
||||||
|
{
|
||||||
|
retries = 0;
|
||||||
|
reconnectTimeout = 0;
|
||||||
|
seconds = -1;
|
||||||
|
lastBytesSent = 0;
|
||||||
|
lastBytesSentTime = os_gettime_ns();
|
||||||
|
delaySecTotal = 0;
|
||||||
|
UpdateDelayMsg();
|
||||||
|
}
|
||||||
|
|
||||||
|
void OBSBasicStatusBar::ReconnectSuccess()
|
||||||
|
{
|
||||||
|
OBSBasic *main = qobject_cast<OBSBasic *>(parent());
|
||||||
|
|
||||||
|
QString msg = QTStr("Basic.StatusBar.ReconnectSuccessful");
|
||||||
|
showMessage(msg, 4000);
|
||||||
|
main->SysTrayNotify(msg, QSystemTrayIcon::Information);
|
||||||
|
ReconnectClear();
|
||||||
|
|
||||||
|
if (streamOutput) {
|
||||||
|
OBSOutput output = OBSGetStrongRef(streamOutput);
|
||||||
|
if (!output)
|
||||||
|
return;
|
||||||
|
|
||||||
|
delaySecTotal = obs_output_get_active_delay(output);
|
||||||
|
UpdateDelayMsg();
|
||||||
|
disconnected = false;
|
||||||
|
firstCongestionUpdate = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void OBSBasicStatusBar::UpdateStatusBar()
|
||||||
|
{
|
||||||
|
OBSBasic *main = qobject_cast<OBSBasic *>(parent());
|
||||||
|
|
||||||
|
UpdateBandwidth();
|
||||||
|
|
||||||
|
if (streamOutput)
|
||||||
|
UpdateStreamTime();
|
||||||
|
|
||||||
|
if (recordOutput)
|
||||||
|
UpdateRecordTime();
|
||||||
|
|
||||||
|
UpdateDroppedFrames();
|
||||||
|
|
||||||
|
int skipped = video_output_get_skipped_frames(obs_get_video());
|
||||||
|
int total = video_output_get_total_frames(obs_get_video());
|
||||||
|
|
||||||
|
skipped -= startSkippedFrameCount;
|
||||||
|
total -= startTotalFrameCount;
|
||||||
|
|
||||||
|
int diff = skipped - lastSkippedFrameCount;
|
||||||
|
double percentage = double(skipped) / double(total) * 100.0;
|
||||||
|
|
||||||
|
if (diff > 10 && percentage >= 0.1f) {
|
||||||
|
showMessage(QTStr("HighResourceUsage"), 4000);
|
||||||
|
if (!main->isVisible() && overloadedNotify) {
|
||||||
|
main->SysTrayNotify(QTStr("HighResourceUsage"), QSystemTrayIcon::Warning);
|
||||||
|
overloadedNotify = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
lastSkippedFrameCount = skipped;
|
||||||
|
}
|
||||||
|
|
||||||
|
void OBSBasicStatusBar::StreamDelayStarting(int sec)
|
||||||
|
{
|
||||||
|
OBSBasic *main = qobject_cast<OBSBasic *>(parent());
|
||||||
|
if (!main || !main->outputHandler)
|
||||||
|
return;
|
||||||
|
|
||||||
|
OBSOutputAutoRelease output = obs_frontend_get_streaming_output();
|
||||||
|
streamOutput = OBSGetWeakRef(output);
|
||||||
|
|
||||||
|
delaySecTotal = delaySecStarting = sec;
|
||||||
|
UpdateDelayMsg();
|
||||||
|
Activate();
|
||||||
|
}
|
||||||
|
|
||||||
|
void OBSBasicStatusBar::StreamDelayStopping(int sec)
|
||||||
|
{
|
||||||
|
delaySecTotal = delaySecStopping = sec;
|
||||||
|
UpdateDelayMsg();
|
||||||
|
}
|
||||||
|
|
||||||
|
void OBSBasicStatusBar::StreamStarted(obs_output_t *output)
|
||||||
|
{
|
||||||
|
streamOutput = OBSGetWeakRef(output);
|
||||||
|
|
||||||
|
streamSigs.emplace_back(obs_output_get_signal_handler(output), "reconnect", OBSOutputReconnect, this);
|
||||||
|
streamSigs.emplace_back(obs_output_get_signal_handler(output), "reconnect_success", OBSOutputReconnectSuccess,
|
||||||
|
this);
|
||||||
|
|
||||||
|
retries = 0;
|
||||||
|
lastBytesSent = 0;
|
||||||
|
lastBytesSentTime = os_gettime_ns();
|
||||||
|
Activate();
|
||||||
|
}
|
||||||
|
|
||||||
|
void OBSBasicStatusBar::StreamStopped()
|
||||||
|
{
|
||||||
|
if (streamOutput) {
|
||||||
|
streamSigs.clear();
|
||||||
|
|
||||||
|
ReconnectClear();
|
||||||
|
streamOutput = nullptr;
|
||||||
|
clearMessage();
|
||||||
|
Deactivate();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void OBSBasicStatusBar::RecordingStarted(obs_output_t *output)
|
||||||
|
{
|
||||||
|
recordOutput = OBSGetWeakRef(output);
|
||||||
|
Activate();
|
||||||
|
}
|
||||||
|
|
||||||
|
void OBSBasicStatusBar::RecordingStopped()
|
||||||
|
{
|
||||||
|
recordOutput = nullptr;
|
||||||
|
Deactivate();
|
||||||
|
}
|
||||||
|
|
||||||
|
void OBSBasicStatusBar::RecordingPaused()
|
||||||
|
{
|
||||||
|
if (recordOutput) {
|
||||||
|
statusWidget->ui->recordIcon->setPixmap(recordingPausePixmap);
|
||||||
|
streamPauseIconToggle = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
UpdateRecordTimeLabel();
|
||||||
|
}
|
||||||
|
|
||||||
|
void OBSBasicStatusBar::RecordingUnpaused()
|
||||||
|
{
|
||||||
|
if (recordOutput) {
|
||||||
|
statusWidget->ui->recordIcon->setPixmap(recordingActivePixmap);
|
||||||
|
}
|
||||||
|
|
||||||
|
UpdateRecordTimeLabel();
|
||||||
|
}
|
||||||
|
|
||||||
|
static QPixmap GetPixmap(const QString &filename)
|
||||||
|
{
|
||||||
|
QString path = obs_frontend_is_theme_dark() ? "theme:Dark/" : ":/res/images/";
|
||||||
|
return QIcon(path + filename).pixmap(QSize(16, 16));
|
||||||
|
}
|
||||||
|
|
||||||
|
void OBSBasicStatusBar::UpdateIcons()
|
||||||
|
{
|
||||||
|
disconnectedPixmap = GetPixmap("network-disconnected.svg");
|
||||||
|
inactivePixmap = GetPixmap("network-inactive.svg");
|
||||||
|
|
||||||
|
streamingInactivePixmap = GetPixmap("streaming-inactive.svg");
|
||||||
|
|
||||||
|
recordingInactivePixmap = GetPixmap("recording-inactive.svg");
|
||||||
|
recordingPauseInactivePixmap = GetPixmap("recording-pause-inactive.svg");
|
||||||
|
|
||||||
|
bool streaming = obs_frontend_streaming_active();
|
||||||
|
|
||||||
|
if (!streaming) {
|
||||||
|
statusWidget->ui->streamIcon->setPixmap(streamingInactivePixmap);
|
||||||
|
statusWidget->ui->statusIcon->setPixmap(inactivePixmap);
|
||||||
|
} else {
|
||||||
|
if (disconnected)
|
||||||
|
statusWidget->ui->statusIcon->setPixmap(disconnectedPixmap);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool recording = obs_frontend_recording_active();
|
||||||
|
|
||||||
|
if (!recording)
|
||||||
|
statusWidget->ui->recordIcon->setPixmap(recordingInactivePixmap);
|
||||||
|
}
|
||||||
|
|
||||||
|
void OBSBasicStatusBar::showMessage(const QString &message, int timeout)
|
||||||
|
{
|
||||||
|
messageTimer->stop();
|
||||||
|
|
||||||
|
statusWidget->ui->message->setText(message);
|
||||||
|
|
||||||
|
if (timeout)
|
||||||
|
messageTimer->start(timeout);
|
||||||
|
}
|
||||||
|
|
||||||
|
void OBSBasicStatusBar::clearMessage()
|
||||||
|
{
|
||||||
|
statusWidget->ui->message->setText("");
|
||||||
|
}
|
119
frontend/widgets/StatusBarWidget.hpp
Normal file
119
frontend/widgets/StatusBarWidget.hpp
Normal file
@ -0,0 +1,119 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <QStatusBar>
|
||||||
|
#include <QPointer>
|
||||||
|
#include <QTimer>
|
||||||
|
#include <obs.h>
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
class Ui_StatusBarWidget;
|
||||||
|
|
||||||
|
class StatusBarWidget : public QWidget {
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
friend class OBSBasicStatusBar;
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::unique_ptr<Ui_StatusBarWidget> ui;
|
||||||
|
|
||||||
|
public:
|
||||||
|
StatusBarWidget(QWidget *parent = nullptr);
|
||||||
|
~StatusBarWidget();
|
||||||
|
};
|
||||||
|
|
||||||
|
class OBSBasicStatusBar : public QStatusBar {
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
private:
|
||||||
|
StatusBarWidget *statusWidget = nullptr;
|
||||||
|
|
||||||
|
OBSWeakOutputAutoRelease streamOutput;
|
||||||
|
std::vector<OBSSignal> streamSigs;
|
||||||
|
OBSWeakOutputAutoRelease recordOutput;
|
||||||
|
bool active = false;
|
||||||
|
bool overloadedNotify = true;
|
||||||
|
bool streamPauseIconToggle = false;
|
||||||
|
bool disconnected = false;
|
||||||
|
bool firstCongestionUpdate = false;
|
||||||
|
|
||||||
|
std::vector<float> congestionArray;
|
||||||
|
|
||||||
|
int retries = 0;
|
||||||
|
int totalStreamSeconds = 0;
|
||||||
|
int totalRecordSeconds = 0;
|
||||||
|
|
||||||
|
int reconnectTimeout = 0;
|
||||||
|
|
||||||
|
int delaySecTotal = 0;
|
||||||
|
int delaySecStarting = 0;
|
||||||
|
int delaySecStopping = 0;
|
||||||
|
|
||||||
|
int startSkippedFrameCount = 0;
|
||||||
|
int startTotalFrameCount = 0;
|
||||||
|
int lastSkippedFrameCount = 0;
|
||||||
|
|
||||||
|
int seconds = 0;
|
||||||
|
uint64_t lastBytesSent = 0;
|
||||||
|
uint64_t lastBytesSentTime = 0;
|
||||||
|
|
||||||
|
QPixmap excellentPixmap;
|
||||||
|
QPixmap goodPixmap;
|
||||||
|
QPixmap mediocrePixmap;
|
||||||
|
QPixmap badPixmap;
|
||||||
|
QPixmap disconnectedPixmap;
|
||||||
|
QPixmap inactivePixmap;
|
||||||
|
|
||||||
|
QPixmap recordingActivePixmap;
|
||||||
|
QPixmap recordingPausePixmap;
|
||||||
|
QPixmap recordingPauseInactivePixmap;
|
||||||
|
QPixmap recordingInactivePixmap;
|
||||||
|
QPixmap streamingActivePixmap;
|
||||||
|
QPixmap streamingInactivePixmap;
|
||||||
|
|
||||||
|
float lastCongestion = 0.0f;
|
||||||
|
|
||||||
|
QPointer<QTimer> refreshTimer;
|
||||||
|
QPointer<QTimer> messageTimer;
|
||||||
|
|
||||||
|
obs_output_t *GetOutput();
|
||||||
|
|
||||||
|
void Activate();
|
||||||
|
void Deactivate();
|
||||||
|
|
||||||
|
void UpdateDelayMsg();
|
||||||
|
void UpdateBandwidth();
|
||||||
|
void UpdateStreamTime();
|
||||||
|
void UpdateRecordTime();
|
||||||
|
void UpdateRecordTimeLabel();
|
||||||
|
void UpdateDroppedFrames();
|
||||||
|
|
||||||
|
static void OBSOutputReconnect(void *data, calldata_t *params);
|
||||||
|
static void OBSOutputReconnectSuccess(void *data, calldata_t *params);
|
||||||
|
|
||||||
|
public slots:
|
||||||
|
void UpdateCPUUsage();
|
||||||
|
|
||||||
|
void clearMessage();
|
||||||
|
void showMessage(const QString &message, int timeout = 0);
|
||||||
|
|
||||||
|
private slots:
|
||||||
|
void Reconnect(int seconds);
|
||||||
|
void ReconnectSuccess();
|
||||||
|
void UpdateStatusBar();
|
||||||
|
void UpdateCurrentFPS();
|
||||||
|
void UpdateIcons();
|
||||||
|
|
||||||
|
public:
|
||||||
|
OBSBasicStatusBar(QWidget *parent);
|
||||||
|
|
||||||
|
void StreamDelayStarting(int sec);
|
||||||
|
void StreamDelayStopping(int sec);
|
||||||
|
void StreamStarted(obs_output_t *output);
|
||||||
|
void StreamStopped();
|
||||||
|
void RecordingStarted(obs_output_t *output);
|
||||||
|
void RecordingStopped();
|
||||||
|
void RecordingPaused();
|
||||||
|
void RecordingUnpaused();
|
||||||
|
|
||||||
|
void ReconnectClear();
|
||||||
|
};
|
1507
frontend/widgets/VolControl.cpp
Normal file
1507
frontend/widgets/VolControl.cpp
Normal file
File diff suppressed because it is too large
Load Diff
341
frontend/widgets/VolControl.hpp
Normal file
341
frontend/widgets/VolControl.hpp
Normal file
@ -0,0 +1,341 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <obs.hpp>
|
||||||
|
#include <QWidget>
|
||||||
|
#include <QPaintEvent>
|
||||||
|
#include <QTimer>
|
||||||
|
#include <QMutex>
|
||||||
|
#include <QList>
|
||||||
|
#include <QMenu>
|
||||||
|
#include <QAccessibleWidget>
|
||||||
|
#include "absolute-slider.hpp"
|
||||||
|
|
||||||
|
class QPushButton;
|
||||||
|
class VolumeMeterTimer;
|
||||||
|
class VolumeSlider;
|
||||||
|
|
||||||
|
class VolumeMeter : public QWidget {
|
||||||
|
Q_OBJECT
|
||||||
|
Q_PROPERTY(QColor backgroundNominalColor READ getBackgroundNominalColor WRITE setBackgroundNominalColor
|
||||||
|
DESIGNABLE true)
|
||||||
|
Q_PROPERTY(QColor backgroundWarningColor READ getBackgroundWarningColor WRITE setBackgroundWarningColor
|
||||||
|
DESIGNABLE true)
|
||||||
|
Q_PROPERTY(
|
||||||
|
QColor backgroundErrorColor READ getBackgroundErrorColor WRITE setBackgroundErrorColor DESIGNABLE true)
|
||||||
|
Q_PROPERTY(QColor foregroundNominalColor READ getForegroundNominalColor WRITE setForegroundNominalColor
|
||||||
|
DESIGNABLE true)
|
||||||
|
Q_PROPERTY(QColor foregroundWarningColor READ getForegroundWarningColor WRITE setForegroundWarningColor
|
||||||
|
DESIGNABLE true)
|
||||||
|
Q_PROPERTY(
|
||||||
|
QColor foregroundErrorColor READ getForegroundErrorColor WRITE setForegroundErrorColor DESIGNABLE true)
|
||||||
|
|
||||||
|
Q_PROPERTY(QColor backgroundNominalColorDisabled READ getBackgroundNominalColorDisabled WRITE
|
||||||
|
setBackgroundNominalColorDisabled DESIGNABLE true)
|
||||||
|
Q_PROPERTY(QColor backgroundWarningColorDisabled READ getBackgroundWarningColorDisabled WRITE
|
||||||
|
setBackgroundWarningColorDisabled DESIGNABLE true)
|
||||||
|
Q_PROPERTY(QColor backgroundErrorColorDisabled READ getBackgroundErrorColorDisabled WRITE
|
||||||
|
setBackgroundErrorColorDisabled DESIGNABLE true)
|
||||||
|
Q_PROPERTY(QColor foregroundNominalColorDisabled READ getForegroundNominalColorDisabled WRITE
|
||||||
|
setForegroundNominalColorDisabled DESIGNABLE true)
|
||||||
|
Q_PROPERTY(QColor foregroundWarningColorDisabled READ getForegroundWarningColorDisabled WRITE
|
||||||
|
setForegroundWarningColorDisabled DESIGNABLE true)
|
||||||
|
Q_PROPERTY(QColor foregroundErrorColorDisabled READ getForegroundErrorColorDisabled WRITE
|
||||||
|
setForegroundErrorColorDisabled DESIGNABLE true)
|
||||||
|
|
||||||
|
Q_PROPERTY(QColor clipColor READ getClipColor WRITE setClipColor DESIGNABLE true)
|
||||||
|
Q_PROPERTY(QColor magnitudeColor READ getMagnitudeColor WRITE setMagnitudeColor DESIGNABLE true)
|
||||||
|
Q_PROPERTY(QColor majorTickColor READ getMajorTickColor WRITE setMajorTickColor DESIGNABLE true)
|
||||||
|
Q_PROPERTY(QColor minorTickColor READ getMinorTickColor WRITE setMinorTickColor DESIGNABLE true)
|
||||||
|
Q_PROPERTY(int meterThickness READ getMeterThickness WRITE setMeterThickness DESIGNABLE true)
|
||||||
|
Q_PROPERTY(qreal meterFontScaling READ getMeterFontScaling WRITE setMeterFontScaling DESIGNABLE true)
|
||||||
|
|
||||||
|
// Levels are denoted in dBFS.
|
||||||
|
Q_PROPERTY(qreal minimumLevel READ getMinimumLevel WRITE setMinimumLevel DESIGNABLE true)
|
||||||
|
Q_PROPERTY(qreal warningLevel READ getWarningLevel WRITE setWarningLevel DESIGNABLE true)
|
||||||
|
Q_PROPERTY(qreal errorLevel READ getErrorLevel WRITE setErrorLevel DESIGNABLE true)
|
||||||
|
Q_PROPERTY(qreal clipLevel READ getClipLevel WRITE setClipLevel DESIGNABLE true)
|
||||||
|
Q_PROPERTY(qreal minimumInputLevel READ getMinimumInputLevel WRITE setMinimumInputLevel DESIGNABLE true)
|
||||||
|
|
||||||
|
// Rates are denoted in dB/second.
|
||||||
|
Q_PROPERTY(qreal peakDecayRate READ getPeakDecayRate WRITE setPeakDecayRate DESIGNABLE true)
|
||||||
|
|
||||||
|
// Time in seconds for the VU meter to integrate over.
|
||||||
|
Q_PROPERTY(qreal magnitudeIntegrationTime READ getMagnitudeIntegrationTime WRITE setMagnitudeIntegrationTime
|
||||||
|
DESIGNABLE true)
|
||||||
|
|
||||||
|
// Duration is denoted in seconds.
|
||||||
|
Q_PROPERTY(qreal peakHoldDuration READ getPeakHoldDuration WRITE setPeakHoldDuration DESIGNABLE true)
|
||||||
|
Q_PROPERTY(qreal inputPeakHoldDuration READ getInputPeakHoldDuration WRITE setInputPeakHoldDuration
|
||||||
|
DESIGNABLE true)
|
||||||
|
|
||||||
|
friend class VolControl;
|
||||||
|
|
||||||
|
private:
|
||||||
|
obs_volmeter_t *obs_volmeter;
|
||||||
|
static std::weak_ptr<VolumeMeterTimer> updateTimer;
|
||||||
|
std::shared_ptr<VolumeMeterTimer> updateTimerRef;
|
||||||
|
|
||||||
|
inline void resetLevels();
|
||||||
|
inline void doLayout();
|
||||||
|
inline bool detectIdle(uint64_t ts);
|
||||||
|
inline void calculateBallistics(uint64_t ts, qreal timeSinceLastRedraw = 0.0);
|
||||||
|
inline void calculateBallisticsForChannel(int channelNr, uint64_t ts, qreal timeSinceLastRedraw);
|
||||||
|
|
||||||
|
inline int convertToInt(float number);
|
||||||
|
void paintInputMeter(QPainter &painter, int x, int y, int width, int height, float peakHold);
|
||||||
|
void paintHMeter(QPainter &painter, int x, int y, int width, int height, float magnitude, float peak,
|
||||||
|
float peakHold);
|
||||||
|
void paintHTicks(QPainter &painter, int x, int y, int width);
|
||||||
|
void paintVMeter(QPainter &painter, int x, int y, int width, int height, float magnitude, float peak,
|
||||||
|
float peakHold);
|
||||||
|
void paintVTicks(QPainter &painter, int x, int y, int height);
|
||||||
|
|
||||||
|
QMutex dataMutex;
|
||||||
|
|
||||||
|
bool recalculateLayout = true;
|
||||||
|
uint64_t currentLastUpdateTime = 0;
|
||||||
|
float currentMagnitude[MAX_AUDIO_CHANNELS];
|
||||||
|
float currentPeak[MAX_AUDIO_CHANNELS];
|
||||||
|
float currentInputPeak[MAX_AUDIO_CHANNELS];
|
||||||
|
|
||||||
|
int displayNrAudioChannels = 0;
|
||||||
|
float displayMagnitude[MAX_AUDIO_CHANNELS];
|
||||||
|
float displayPeak[MAX_AUDIO_CHANNELS];
|
||||||
|
float displayPeakHold[MAX_AUDIO_CHANNELS];
|
||||||
|
uint64_t displayPeakHoldLastUpdateTime[MAX_AUDIO_CHANNELS];
|
||||||
|
float displayInputPeakHold[MAX_AUDIO_CHANNELS];
|
||||||
|
uint64_t displayInputPeakHoldLastUpdateTime[MAX_AUDIO_CHANNELS];
|
||||||
|
|
||||||
|
QFont tickFont;
|
||||||
|
QColor backgroundNominalColor;
|
||||||
|
QColor backgroundWarningColor;
|
||||||
|
QColor backgroundErrorColor;
|
||||||
|
QColor foregroundNominalColor;
|
||||||
|
QColor foregroundWarningColor;
|
||||||
|
QColor foregroundErrorColor;
|
||||||
|
|
||||||
|
QColor backgroundNominalColorDisabled;
|
||||||
|
QColor backgroundWarningColorDisabled;
|
||||||
|
QColor backgroundErrorColorDisabled;
|
||||||
|
QColor foregroundNominalColorDisabled;
|
||||||
|
QColor foregroundWarningColorDisabled;
|
||||||
|
QColor foregroundErrorColorDisabled;
|
||||||
|
|
||||||
|
QColor clipColor;
|
||||||
|
QColor magnitudeColor;
|
||||||
|
QColor majorTickColor;
|
||||||
|
QColor minorTickColor;
|
||||||
|
|
||||||
|
int meterThickness;
|
||||||
|
qreal meterFontScaling;
|
||||||
|
|
||||||
|
qreal minimumLevel;
|
||||||
|
qreal warningLevel;
|
||||||
|
qreal errorLevel;
|
||||||
|
qreal clipLevel;
|
||||||
|
qreal minimumInputLevel;
|
||||||
|
qreal peakDecayRate;
|
||||||
|
qreal magnitudeIntegrationTime;
|
||||||
|
qreal peakHoldDuration;
|
||||||
|
qreal inputPeakHoldDuration;
|
||||||
|
|
||||||
|
QColor p_backgroundNominalColor;
|
||||||
|
QColor p_backgroundWarningColor;
|
||||||
|
QColor p_backgroundErrorColor;
|
||||||
|
QColor p_foregroundNominalColor;
|
||||||
|
QColor p_foregroundWarningColor;
|
||||||
|
QColor p_foregroundErrorColor;
|
||||||
|
|
||||||
|
uint64_t lastRedrawTime = 0;
|
||||||
|
int channels = 0;
|
||||||
|
bool clipping = false;
|
||||||
|
bool vertical;
|
||||||
|
bool muted = false;
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit VolumeMeter(QWidget *parent = nullptr, obs_volmeter_t *obs_volmeter = nullptr, bool vertical = false);
|
||||||
|
~VolumeMeter();
|
||||||
|
|
||||||
|
void setLevels(const float magnitude[MAX_AUDIO_CHANNELS], const float peak[MAX_AUDIO_CHANNELS],
|
||||||
|
const float inputPeak[MAX_AUDIO_CHANNELS]);
|
||||||
|
QRect getBarRect() const;
|
||||||
|
bool needLayoutChange();
|
||||||
|
|
||||||
|
QColor getBackgroundNominalColor() const;
|
||||||
|
void setBackgroundNominalColor(QColor c);
|
||||||
|
QColor getBackgroundWarningColor() const;
|
||||||
|
void setBackgroundWarningColor(QColor c);
|
||||||
|
QColor getBackgroundErrorColor() const;
|
||||||
|
void setBackgroundErrorColor(QColor c);
|
||||||
|
QColor getForegroundNominalColor() const;
|
||||||
|
void setForegroundNominalColor(QColor c);
|
||||||
|
QColor getForegroundWarningColor() const;
|
||||||
|
void setForegroundWarningColor(QColor c);
|
||||||
|
QColor getForegroundErrorColor() const;
|
||||||
|
void setForegroundErrorColor(QColor c);
|
||||||
|
|
||||||
|
QColor getBackgroundNominalColorDisabled() const;
|
||||||
|
void setBackgroundNominalColorDisabled(QColor c);
|
||||||
|
QColor getBackgroundWarningColorDisabled() const;
|
||||||
|
void setBackgroundWarningColorDisabled(QColor c);
|
||||||
|
QColor getBackgroundErrorColorDisabled() const;
|
||||||
|
void setBackgroundErrorColorDisabled(QColor c);
|
||||||
|
QColor getForegroundNominalColorDisabled() const;
|
||||||
|
void setForegroundNominalColorDisabled(QColor c);
|
||||||
|
QColor getForegroundWarningColorDisabled() const;
|
||||||
|
void setForegroundWarningColorDisabled(QColor c);
|
||||||
|
QColor getForegroundErrorColorDisabled() const;
|
||||||
|
void setForegroundErrorColorDisabled(QColor c);
|
||||||
|
|
||||||
|
QColor getClipColor() const;
|
||||||
|
void setClipColor(QColor c);
|
||||||
|
QColor getMagnitudeColor() const;
|
||||||
|
void setMagnitudeColor(QColor c);
|
||||||
|
QColor getMajorTickColor() const;
|
||||||
|
void setMajorTickColor(QColor c);
|
||||||
|
QColor getMinorTickColor() const;
|
||||||
|
void setMinorTickColor(QColor c);
|
||||||
|
int getMeterThickness() const;
|
||||||
|
void setMeterThickness(int v);
|
||||||
|
qreal getMeterFontScaling() const;
|
||||||
|
void setMeterFontScaling(qreal v);
|
||||||
|
qreal getMinimumLevel() const;
|
||||||
|
void setMinimumLevel(qreal v);
|
||||||
|
qreal getWarningLevel() const;
|
||||||
|
void setWarningLevel(qreal v);
|
||||||
|
qreal getErrorLevel() const;
|
||||||
|
void setErrorLevel(qreal v);
|
||||||
|
qreal getClipLevel() const;
|
||||||
|
void setClipLevel(qreal v);
|
||||||
|
qreal getMinimumInputLevel() const;
|
||||||
|
void setMinimumInputLevel(qreal v);
|
||||||
|
qreal getPeakDecayRate() const;
|
||||||
|
void setPeakDecayRate(qreal v);
|
||||||
|
qreal getMagnitudeIntegrationTime() const;
|
||||||
|
void setMagnitudeIntegrationTime(qreal v);
|
||||||
|
qreal getPeakHoldDuration() const;
|
||||||
|
void setPeakHoldDuration(qreal v);
|
||||||
|
qreal getInputPeakHoldDuration() const;
|
||||||
|
void setInputPeakHoldDuration(qreal v);
|
||||||
|
void setPeakMeterType(enum obs_peak_meter_type peakMeterType);
|
||||||
|
virtual void mousePressEvent(QMouseEvent *event) override;
|
||||||
|
virtual void wheelEvent(QWheelEvent *event) override;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void paintEvent(QPaintEvent *event) override;
|
||||||
|
void changeEvent(QEvent *e) override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class VolumeMeterTimer : public QTimer {
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
inline VolumeMeterTimer() : QTimer() {}
|
||||||
|
|
||||||
|
void AddVolControl(VolumeMeter *meter);
|
||||||
|
void RemoveVolControl(VolumeMeter *meter);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void timerEvent(QTimerEvent *event) override;
|
||||||
|
QList<VolumeMeter *> volumeMeters;
|
||||||
|
};
|
||||||
|
|
||||||
|
class QLabel;
|
||||||
|
class VolumeSlider;
|
||||||
|
class MuteCheckBox;
|
||||||
|
class OBSSourceLabel;
|
||||||
|
|
||||||
|
class VolControl : public QFrame {
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
private:
|
||||||
|
OBSSource source;
|
||||||
|
std::vector<OBSSignal> sigs;
|
||||||
|
OBSSourceLabel *nameLabel;
|
||||||
|
QLabel *volLabel;
|
||||||
|
VolumeMeter *volMeter;
|
||||||
|
VolumeSlider *slider;
|
||||||
|
MuteCheckBox *mute;
|
||||||
|
QPushButton *config = nullptr;
|
||||||
|
float levelTotal;
|
||||||
|
float levelCount;
|
||||||
|
OBSFader obs_fader;
|
||||||
|
OBSVolMeter obs_volmeter;
|
||||||
|
bool vertical;
|
||||||
|
QMenu *contextMenu;
|
||||||
|
|
||||||
|
static void OBSVolumeChanged(void *param, float db);
|
||||||
|
static void OBSVolumeLevel(void *data, const float magnitude[MAX_AUDIO_CHANNELS],
|
||||||
|
const float peak[MAX_AUDIO_CHANNELS], const float inputPeak[MAX_AUDIO_CHANNELS]);
|
||||||
|
static void OBSVolumeMuted(void *data, calldata_t *calldata);
|
||||||
|
static void OBSMixersOrMonitoringChanged(void *data, calldata_t *);
|
||||||
|
|
||||||
|
void EmitConfigClicked();
|
||||||
|
|
||||||
|
private slots:
|
||||||
|
void VolumeChanged();
|
||||||
|
void VolumeMuted(bool muted);
|
||||||
|
void MixersOrMonitoringChanged();
|
||||||
|
|
||||||
|
void SetMuted(bool checked);
|
||||||
|
void SliderChanged(int vol);
|
||||||
|
void updateText();
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void ConfigClicked();
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit VolControl(OBSSource source, bool showConfig = false, bool vertical = false);
|
||||||
|
~VolControl();
|
||||||
|
|
||||||
|
inline obs_source_t *GetSource() const { return source; }
|
||||||
|
|
||||||
|
void SetMeterDecayRate(qreal q);
|
||||||
|
void setPeakMeterType(enum obs_peak_meter_type peakMeterType);
|
||||||
|
|
||||||
|
void EnableSlider(bool enable);
|
||||||
|
inline void SetContextMenu(QMenu *cm) { contextMenu = cm; }
|
||||||
|
|
||||||
|
void refreshColors();
|
||||||
|
};
|
||||||
|
|
||||||
|
class VolumeSlider : public AbsoluteSlider {
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
obs_fader_t *fad;
|
||||||
|
|
||||||
|
VolumeSlider(obs_fader_t *fader, QWidget *parent = nullptr);
|
||||||
|
VolumeSlider(obs_fader_t *fader, Qt::Orientation orientation, QWidget *parent = nullptr);
|
||||||
|
|
||||||
|
bool getDisplayTicks() const;
|
||||||
|
void setDisplayTicks(bool display);
|
||||||
|
|
||||||
|
private:
|
||||||
|
bool displayTicks = false;
|
||||||
|
QColor tickColor;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
virtual void paintEvent(QPaintEvent *event) override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class VolumeAccessibleInterface : public QAccessibleWidget {
|
||||||
|
|
||||||
|
public:
|
||||||
|
VolumeAccessibleInterface(QWidget *w);
|
||||||
|
|
||||||
|
QVariant currentValue() const;
|
||||||
|
void setCurrentValue(const QVariant &value);
|
||||||
|
|
||||||
|
QVariant maximumValue() const;
|
||||||
|
QVariant minimumValue() const;
|
||||||
|
|
||||||
|
QVariant minimumStepSize() const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
VolumeSlider *slider() const;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
virtual QAccessible::Role role() const override;
|
||||||
|
virtual QString text(QAccessible::Text t) const override;
|
||||||
|
};
|
1507
frontend/widgets/VolumeAccessibleInterface.cpp
Normal file
1507
frontend/widgets/VolumeAccessibleInterface.cpp
Normal file
File diff suppressed because it is too large
Load Diff
341
frontend/widgets/VolumeAccessibleInterface.hpp
Normal file
341
frontend/widgets/VolumeAccessibleInterface.hpp
Normal file
@ -0,0 +1,341 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <obs.hpp>
|
||||||
|
#include <QWidget>
|
||||||
|
#include <QPaintEvent>
|
||||||
|
#include <QTimer>
|
||||||
|
#include <QMutex>
|
||||||
|
#include <QList>
|
||||||
|
#include <QMenu>
|
||||||
|
#include <QAccessibleWidget>
|
||||||
|
#include "absolute-slider.hpp"
|
||||||
|
|
||||||
|
class QPushButton;
|
||||||
|
class VolumeMeterTimer;
|
||||||
|
class VolumeSlider;
|
||||||
|
|
||||||
|
class VolumeMeter : public QWidget {
|
||||||
|
Q_OBJECT
|
||||||
|
Q_PROPERTY(QColor backgroundNominalColor READ getBackgroundNominalColor WRITE setBackgroundNominalColor
|
||||||
|
DESIGNABLE true)
|
||||||
|
Q_PROPERTY(QColor backgroundWarningColor READ getBackgroundWarningColor WRITE setBackgroundWarningColor
|
||||||
|
DESIGNABLE true)
|
||||||
|
Q_PROPERTY(
|
||||||
|
QColor backgroundErrorColor READ getBackgroundErrorColor WRITE setBackgroundErrorColor DESIGNABLE true)
|
||||||
|
Q_PROPERTY(QColor foregroundNominalColor READ getForegroundNominalColor WRITE setForegroundNominalColor
|
||||||
|
DESIGNABLE true)
|
||||||
|
Q_PROPERTY(QColor foregroundWarningColor READ getForegroundWarningColor WRITE setForegroundWarningColor
|
||||||
|
DESIGNABLE true)
|
||||||
|
Q_PROPERTY(
|
||||||
|
QColor foregroundErrorColor READ getForegroundErrorColor WRITE setForegroundErrorColor DESIGNABLE true)
|
||||||
|
|
||||||
|
Q_PROPERTY(QColor backgroundNominalColorDisabled READ getBackgroundNominalColorDisabled WRITE
|
||||||
|
setBackgroundNominalColorDisabled DESIGNABLE true)
|
||||||
|
Q_PROPERTY(QColor backgroundWarningColorDisabled READ getBackgroundWarningColorDisabled WRITE
|
||||||
|
setBackgroundWarningColorDisabled DESIGNABLE true)
|
||||||
|
Q_PROPERTY(QColor backgroundErrorColorDisabled READ getBackgroundErrorColorDisabled WRITE
|
||||||
|
setBackgroundErrorColorDisabled DESIGNABLE true)
|
||||||
|
Q_PROPERTY(QColor foregroundNominalColorDisabled READ getForegroundNominalColorDisabled WRITE
|
||||||
|
setForegroundNominalColorDisabled DESIGNABLE true)
|
||||||
|
Q_PROPERTY(QColor foregroundWarningColorDisabled READ getForegroundWarningColorDisabled WRITE
|
||||||
|
setForegroundWarningColorDisabled DESIGNABLE true)
|
||||||
|
Q_PROPERTY(QColor foregroundErrorColorDisabled READ getForegroundErrorColorDisabled WRITE
|
||||||
|
setForegroundErrorColorDisabled DESIGNABLE true)
|
||||||
|
|
||||||
|
Q_PROPERTY(QColor clipColor READ getClipColor WRITE setClipColor DESIGNABLE true)
|
||||||
|
Q_PROPERTY(QColor magnitudeColor READ getMagnitudeColor WRITE setMagnitudeColor DESIGNABLE true)
|
||||||
|
Q_PROPERTY(QColor majorTickColor READ getMajorTickColor WRITE setMajorTickColor DESIGNABLE true)
|
||||||
|
Q_PROPERTY(QColor minorTickColor READ getMinorTickColor WRITE setMinorTickColor DESIGNABLE true)
|
||||||
|
Q_PROPERTY(int meterThickness READ getMeterThickness WRITE setMeterThickness DESIGNABLE true)
|
||||||
|
Q_PROPERTY(qreal meterFontScaling READ getMeterFontScaling WRITE setMeterFontScaling DESIGNABLE true)
|
||||||
|
|
||||||
|
// Levels are denoted in dBFS.
|
||||||
|
Q_PROPERTY(qreal minimumLevel READ getMinimumLevel WRITE setMinimumLevel DESIGNABLE true)
|
||||||
|
Q_PROPERTY(qreal warningLevel READ getWarningLevel WRITE setWarningLevel DESIGNABLE true)
|
||||||
|
Q_PROPERTY(qreal errorLevel READ getErrorLevel WRITE setErrorLevel DESIGNABLE true)
|
||||||
|
Q_PROPERTY(qreal clipLevel READ getClipLevel WRITE setClipLevel DESIGNABLE true)
|
||||||
|
Q_PROPERTY(qreal minimumInputLevel READ getMinimumInputLevel WRITE setMinimumInputLevel DESIGNABLE true)
|
||||||
|
|
||||||
|
// Rates are denoted in dB/second.
|
||||||
|
Q_PROPERTY(qreal peakDecayRate READ getPeakDecayRate WRITE setPeakDecayRate DESIGNABLE true)
|
||||||
|
|
||||||
|
// Time in seconds for the VU meter to integrate over.
|
||||||
|
Q_PROPERTY(qreal magnitudeIntegrationTime READ getMagnitudeIntegrationTime WRITE setMagnitudeIntegrationTime
|
||||||
|
DESIGNABLE true)
|
||||||
|
|
||||||
|
// Duration is denoted in seconds.
|
||||||
|
Q_PROPERTY(qreal peakHoldDuration READ getPeakHoldDuration WRITE setPeakHoldDuration DESIGNABLE true)
|
||||||
|
Q_PROPERTY(qreal inputPeakHoldDuration READ getInputPeakHoldDuration WRITE setInputPeakHoldDuration
|
||||||
|
DESIGNABLE true)
|
||||||
|
|
||||||
|
friend class VolControl;
|
||||||
|
|
||||||
|
private:
|
||||||
|
obs_volmeter_t *obs_volmeter;
|
||||||
|
static std::weak_ptr<VolumeMeterTimer> updateTimer;
|
||||||
|
std::shared_ptr<VolumeMeterTimer> updateTimerRef;
|
||||||
|
|
||||||
|
inline void resetLevels();
|
||||||
|
inline void doLayout();
|
||||||
|
inline bool detectIdle(uint64_t ts);
|
||||||
|
inline void calculateBallistics(uint64_t ts, qreal timeSinceLastRedraw = 0.0);
|
||||||
|
inline void calculateBallisticsForChannel(int channelNr, uint64_t ts, qreal timeSinceLastRedraw);
|
||||||
|
|
||||||
|
inline int convertToInt(float number);
|
||||||
|
void paintInputMeter(QPainter &painter, int x, int y, int width, int height, float peakHold);
|
||||||
|
void paintHMeter(QPainter &painter, int x, int y, int width, int height, float magnitude, float peak,
|
||||||
|
float peakHold);
|
||||||
|
void paintHTicks(QPainter &painter, int x, int y, int width);
|
||||||
|
void paintVMeter(QPainter &painter, int x, int y, int width, int height, float magnitude, float peak,
|
||||||
|
float peakHold);
|
||||||
|
void paintVTicks(QPainter &painter, int x, int y, int height);
|
||||||
|
|
||||||
|
QMutex dataMutex;
|
||||||
|
|
||||||
|
bool recalculateLayout = true;
|
||||||
|
uint64_t currentLastUpdateTime = 0;
|
||||||
|
float currentMagnitude[MAX_AUDIO_CHANNELS];
|
||||||
|
float currentPeak[MAX_AUDIO_CHANNELS];
|
||||||
|
float currentInputPeak[MAX_AUDIO_CHANNELS];
|
||||||
|
|
||||||
|
int displayNrAudioChannels = 0;
|
||||||
|
float displayMagnitude[MAX_AUDIO_CHANNELS];
|
||||||
|
float displayPeak[MAX_AUDIO_CHANNELS];
|
||||||
|
float displayPeakHold[MAX_AUDIO_CHANNELS];
|
||||||
|
uint64_t displayPeakHoldLastUpdateTime[MAX_AUDIO_CHANNELS];
|
||||||
|
float displayInputPeakHold[MAX_AUDIO_CHANNELS];
|
||||||
|
uint64_t displayInputPeakHoldLastUpdateTime[MAX_AUDIO_CHANNELS];
|
||||||
|
|
||||||
|
QFont tickFont;
|
||||||
|
QColor backgroundNominalColor;
|
||||||
|
QColor backgroundWarningColor;
|
||||||
|
QColor backgroundErrorColor;
|
||||||
|
QColor foregroundNominalColor;
|
||||||
|
QColor foregroundWarningColor;
|
||||||
|
QColor foregroundErrorColor;
|
||||||
|
|
||||||
|
QColor backgroundNominalColorDisabled;
|
||||||
|
QColor backgroundWarningColorDisabled;
|
||||||
|
QColor backgroundErrorColorDisabled;
|
||||||
|
QColor foregroundNominalColorDisabled;
|
||||||
|
QColor foregroundWarningColorDisabled;
|
||||||
|
QColor foregroundErrorColorDisabled;
|
||||||
|
|
||||||
|
QColor clipColor;
|
||||||
|
QColor magnitudeColor;
|
||||||
|
QColor majorTickColor;
|
||||||
|
QColor minorTickColor;
|
||||||
|
|
||||||
|
int meterThickness;
|
||||||
|
qreal meterFontScaling;
|
||||||
|
|
||||||
|
qreal minimumLevel;
|
||||||
|
qreal warningLevel;
|
||||||
|
qreal errorLevel;
|
||||||
|
qreal clipLevel;
|
||||||
|
qreal minimumInputLevel;
|
||||||
|
qreal peakDecayRate;
|
||||||
|
qreal magnitudeIntegrationTime;
|
||||||
|
qreal peakHoldDuration;
|
||||||
|
qreal inputPeakHoldDuration;
|
||||||
|
|
||||||
|
QColor p_backgroundNominalColor;
|
||||||
|
QColor p_backgroundWarningColor;
|
||||||
|
QColor p_backgroundErrorColor;
|
||||||
|
QColor p_foregroundNominalColor;
|
||||||
|
QColor p_foregroundWarningColor;
|
||||||
|
QColor p_foregroundErrorColor;
|
||||||
|
|
||||||
|
uint64_t lastRedrawTime = 0;
|
||||||
|
int channels = 0;
|
||||||
|
bool clipping = false;
|
||||||
|
bool vertical;
|
||||||
|
bool muted = false;
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit VolumeMeter(QWidget *parent = nullptr, obs_volmeter_t *obs_volmeter = nullptr, bool vertical = false);
|
||||||
|
~VolumeMeter();
|
||||||
|
|
||||||
|
void setLevels(const float magnitude[MAX_AUDIO_CHANNELS], const float peak[MAX_AUDIO_CHANNELS],
|
||||||
|
const float inputPeak[MAX_AUDIO_CHANNELS]);
|
||||||
|
QRect getBarRect() const;
|
||||||
|
bool needLayoutChange();
|
||||||
|
|
||||||
|
QColor getBackgroundNominalColor() const;
|
||||||
|
void setBackgroundNominalColor(QColor c);
|
||||||
|
QColor getBackgroundWarningColor() const;
|
||||||
|
void setBackgroundWarningColor(QColor c);
|
||||||
|
QColor getBackgroundErrorColor() const;
|
||||||
|
void setBackgroundErrorColor(QColor c);
|
||||||
|
QColor getForegroundNominalColor() const;
|
||||||
|
void setForegroundNominalColor(QColor c);
|
||||||
|
QColor getForegroundWarningColor() const;
|
||||||
|
void setForegroundWarningColor(QColor c);
|
||||||
|
QColor getForegroundErrorColor() const;
|
||||||
|
void setForegroundErrorColor(QColor c);
|
||||||
|
|
||||||
|
QColor getBackgroundNominalColorDisabled() const;
|
||||||
|
void setBackgroundNominalColorDisabled(QColor c);
|
||||||
|
QColor getBackgroundWarningColorDisabled() const;
|
||||||
|
void setBackgroundWarningColorDisabled(QColor c);
|
||||||
|
QColor getBackgroundErrorColorDisabled() const;
|
||||||
|
void setBackgroundErrorColorDisabled(QColor c);
|
||||||
|
QColor getForegroundNominalColorDisabled() const;
|
||||||
|
void setForegroundNominalColorDisabled(QColor c);
|
||||||
|
QColor getForegroundWarningColorDisabled() const;
|
||||||
|
void setForegroundWarningColorDisabled(QColor c);
|
||||||
|
QColor getForegroundErrorColorDisabled() const;
|
||||||
|
void setForegroundErrorColorDisabled(QColor c);
|
||||||
|
|
||||||
|
QColor getClipColor() const;
|
||||||
|
void setClipColor(QColor c);
|
||||||
|
QColor getMagnitudeColor() const;
|
||||||
|
void setMagnitudeColor(QColor c);
|
||||||
|
QColor getMajorTickColor() const;
|
||||||
|
void setMajorTickColor(QColor c);
|
||||||
|
QColor getMinorTickColor() const;
|
||||||
|
void setMinorTickColor(QColor c);
|
||||||
|
int getMeterThickness() const;
|
||||||
|
void setMeterThickness(int v);
|
||||||
|
qreal getMeterFontScaling() const;
|
||||||
|
void setMeterFontScaling(qreal v);
|
||||||
|
qreal getMinimumLevel() const;
|
||||||
|
void setMinimumLevel(qreal v);
|
||||||
|
qreal getWarningLevel() const;
|
||||||
|
void setWarningLevel(qreal v);
|
||||||
|
qreal getErrorLevel() const;
|
||||||
|
void setErrorLevel(qreal v);
|
||||||
|
qreal getClipLevel() const;
|
||||||
|
void setClipLevel(qreal v);
|
||||||
|
qreal getMinimumInputLevel() const;
|
||||||
|
void setMinimumInputLevel(qreal v);
|
||||||
|
qreal getPeakDecayRate() const;
|
||||||
|
void setPeakDecayRate(qreal v);
|
||||||
|
qreal getMagnitudeIntegrationTime() const;
|
||||||
|
void setMagnitudeIntegrationTime(qreal v);
|
||||||
|
qreal getPeakHoldDuration() const;
|
||||||
|
void setPeakHoldDuration(qreal v);
|
||||||
|
qreal getInputPeakHoldDuration() const;
|
||||||
|
void setInputPeakHoldDuration(qreal v);
|
||||||
|
void setPeakMeterType(enum obs_peak_meter_type peakMeterType);
|
||||||
|
virtual void mousePressEvent(QMouseEvent *event) override;
|
||||||
|
virtual void wheelEvent(QWheelEvent *event) override;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void paintEvent(QPaintEvent *event) override;
|
||||||
|
void changeEvent(QEvent *e) override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class VolumeMeterTimer : public QTimer {
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
inline VolumeMeterTimer() : QTimer() {}
|
||||||
|
|
||||||
|
void AddVolControl(VolumeMeter *meter);
|
||||||
|
void RemoveVolControl(VolumeMeter *meter);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void timerEvent(QTimerEvent *event) override;
|
||||||
|
QList<VolumeMeter *> volumeMeters;
|
||||||
|
};
|
||||||
|
|
||||||
|
class QLabel;
|
||||||
|
class VolumeSlider;
|
||||||
|
class MuteCheckBox;
|
||||||
|
class OBSSourceLabel;
|
||||||
|
|
||||||
|
class VolControl : public QFrame {
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
private:
|
||||||
|
OBSSource source;
|
||||||
|
std::vector<OBSSignal> sigs;
|
||||||
|
OBSSourceLabel *nameLabel;
|
||||||
|
QLabel *volLabel;
|
||||||
|
VolumeMeter *volMeter;
|
||||||
|
VolumeSlider *slider;
|
||||||
|
MuteCheckBox *mute;
|
||||||
|
QPushButton *config = nullptr;
|
||||||
|
float levelTotal;
|
||||||
|
float levelCount;
|
||||||
|
OBSFader obs_fader;
|
||||||
|
OBSVolMeter obs_volmeter;
|
||||||
|
bool vertical;
|
||||||
|
QMenu *contextMenu;
|
||||||
|
|
||||||
|
static void OBSVolumeChanged(void *param, float db);
|
||||||
|
static void OBSVolumeLevel(void *data, const float magnitude[MAX_AUDIO_CHANNELS],
|
||||||
|
const float peak[MAX_AUDIO_CHANNELS], const float inputPeak[MAX_AUDIO_CHANNELS]);
|
||||||
|
static void OBSVolumeMuted(void *data, calldata_t *calldata);
|
||||||
|
static void OBSMixersOrMonitoringChanged(void *data, calldata_t *);
|
||||||
|
|
||||||
|
void EmitConfigClicked();
|
||||||
|
|
||||||
|
private slots:
|
||||||
|
void VolumeChanged();
|
||||||
|
void VolumeMuted(bool muted);
|
||||||
|
void MixersOrMonitoringChanged();
|
||||||
|
|
||||||
|
void SetMuted(bool checked);
|
||||||
|
void SliderChanged(int vol);
|
||||||
|
void updateText();
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void ConfigClicked();
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit VolControl(OBSSource source, bool showConfig = false, bool vertical = false);
|
||||||
|
~VolControl();
|
||||||
|
|
||||||
|
inline obs_source_t *GetSource() const { return source; }
|
||||||
|
|
||||||
|
void SetMeterDecayRate(qreal q);
|
||||||
|
void setPeakMeterType(enum obs_peak_meter_type peakMeterType);
|
||||||
|
|
||||||
|
void EnableSlider(bool enable);
|
||||||
|
inline void SetContextMenu(QMenu *cm) { contextMenu = cm; }
|
||||||
|
|
||||||
|
void refreshColors();
|
||||||
|
};
|
||||||
|
|
||||||
|
class VolumeSlider : public AbsoluteSlider {
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
obs_fader_t *fad;
|
||||||
|
|
||||||
|
VolumeSlider(obs_fader_t *fader, QWidget *parent = nullptr);
|
||||||
|
VolumeSlider(obs_fader_t *fader, Qt::Orientation orientation, QWidget *parent = nullptr);
|
||||||
|
|
||||||
|
bool getDisplayTicks() const;
|
||||||
|
void setDisplayTicks(bool display);
|
||||||
|
|
||||||
|
private:
|
||||||
|
bool displayTicks = false;
|
||||||
|
QColor tickColor;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
virtual void paintEvent(QPaintEvent *event) override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class VolumeAccessibleInterface : public QAccessibleWidget {
|
||||||
|
|
||||||
|
public:
|
||||||
|
VolumeAccessibleInterface(QWidget *w);
|
||||||
|
|
||||||
|
QVariant currentValue() const;
|
||||||
|
void setCurrentValue(const QVariant &value);
|
||||||
|
|
||||||
|
QVariant maximumValue() const;
|
||||||
|
QVariant minimumValue() const;
|
||||||
|
|
||||||
|
QVariant minimumStepSize() const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
VolumeSlider *slider() const;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
virtual QAccessible::Role role() const override;
|
||||||
|
virtual QString text(QAccessible::Text t) const override;
|
||||||
|
};
|
1507
frontend/widgets/VolumeMeter.cpp
Normal file
1507
frontend/widgets/VolumeMeter.cpp
Normal file
File diff suppressed because it is too large
Load Diff
341
frontend/widgets/VolumeMeter.hpp
Normal file
341
frontend/widgets/VolumeMeter.hpp
Normal file
@ -0,0 +1,341 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <obs.hpp>
|
||||||
|
#include <QWidget>
|
||||||
|
#include <QPaintEvent>
|
||||||
|
#include <QTimer>
|
||||||
|
#include <QMutex>
|
||||||
|
#include <QList>
|
||||||
|
#include <QMenu>
|
||||||
|
#include <QAccessibleWidget>
|
||||||
|
#include "absolute-slider.hpp"
|
||||||
|
|
||||||
|
class QPushButton;
|
||||||
|
class VolumeMeterTimer;
|
||||||
|
class VolumeSlider;
|
||||||
|
|
||||||
|
class VolumeMeter : public QWidget {
|
||||||
|
Q_OBJECT
|
||||||
|
Q_PROPERTY(QColor backgroundNominalColor READ getBackgroundNominalColor WRITE setBackgroundNominalColor
|
||||||
|
DESIGNABLE true)
|
||||||
|
Q_PROPERTY(QColor backgroundWarningColor READ getBackgroundWarningColor WRITE setBackgroundWarningColor
|
||||||
|
DESIGNABLE true)
|
||||||
|
Q_PROPERTY(
|
||||||
|
QColor backgroundErrorColor READ getBackgroundErrorColor WRITE setBackgroundErrorColor DESIGNABLE true)
|
||||||
|
Q_PROPERTY(QColor foregroundNominalColor READ getForegroundNominalColor WRITE setForegroundNominalColor
|
||||||
|
DESIGNABLE true)
|
||||||
|
Q_PROPERTY(QColor foregroundWarningColor READ getForegroundWarningColor WRITE setForegroundWarningColor
|
||||||
|
DESIGNABLE true)
|
||||||
|
Q_PROPERTY(
|
||||||
|
QColor foregroundErrorColor READ getForegroundErrorColor WRITE setForegroundErrorColor DESIGNABLE true)
|
||||||
|
|
||||||
|
Q_PROPERTY(QColor backgroundNominalColorDisabled READ getBackgroundNominalColorDisabled WRITE
|
||||||
|
setBackgroundNominalColorDisabled DESIGNABLE true)
|
||||||
|
Q_PROPERTY(QColor backgroundWarningColorDisabled READ getBackgroundWarningColorDisabled WRITE
|
||||||
|
setBackgroundWarningColorDisabled DESIGNABLE true)
|
||||||
|
Q_PROPERTY(QColor backgroundErrorColorDisabled READ getBackgroundErrorColorDisabled WRITE
|
||||||
|
setBackgroundErrorColorDisabled DESIGNABLE true)
|
||||||
|
Q_PROPERTY(QColor foregroundNominalColorDisabled READ getForegroundNominalColorDisabled WRITE
|
||||||
|
setForegroundNominalColorDisabled DESIGNABLE true)
|
||||||
|
Q_PROPERTY(QColor foregroundWarningColorDisabled READ getForegroundWarningColorDisabled WRITE
|
||||||
|
setForegroundWarningColorDisabled DESIGNABLE true)
|
||||||
|
Q_PROPERTY(QColor foregroundErrorColorDisabled READ getForegroundErrorColorDisabled WRITE
|
||||||
|
setForegroundErrorColorDisabled DESIGNABLE true)
|
||||||
|
|
||||||
|
Q_PROPERTY(QColor clipColor READ getClipColor WRITE setClipColor DESIGNABLE true)
|
||||||
|
Q_PROPERTY(QColor magnitudeColor READ getMagnitudeColor WRITE setMagnitudeColor DESIGNABLE true)
|
||||||
|
Q_PROPERTY(QColor majorTickColor READ getMajorTickColor WRITE setMajorTickColor DESIGNABLE true)
|
||||||
|
Q_PROPERTY(QColor minorTickColor READ getMinorTickColor WRITE setMinorTickColor DESIGNABLE true)
|
||||||
|
Q_PROPERTY(int meterThickness READ getMeterThickness WRITE setMeterThickness DESIGNABLE true)
|
||||||
|
Q_PROPERTY(qreal meterFontScaling READ getMeterFontScaling WRITE setMeterFontScaling DESIGNABLE true)
|
||||||
|
|
||||||
|
// Levels are denoted in dBFS.
|
||||||
|
Q_PROPERTY(qreal minimumLevel READ getMinimumLevel WRITE setMinimumLevel DESIGNABLE true)
|
||||||
|
Q_PROPERTY(qreal warningLevel READ getWarningLevel WRITE setWarningLevel DESIGNABLE true)
|
||||||
|
Q_PROPERTY(qreal errorLevel READ getErrorLevel WRITE setErrorLevel DESIGNABLE true)
|
||||||
|
Q_PROPERTY(qreal clipLevel READ getClipLevel WRITE setClipLevel DESIGNABLE true)
|
||||||
|
Q_PROPERTY(qreal minimumInputLevel READ getMinimumInputLevel WRITE setMinimumInputLevel DESIGNABLE true)
|
||||||
|
|
||||||
|
// Rates are denoted in dB/second.
|
||||||
|
Q_PROPERTY(qreal peakDecayRate READ getPeakDecayRate WRITE setPeakDecayRate DESIGNABLE true)
|
||||||
|
|
||||||
|
// Time in seconds for the VU meter to integrate over.
|
||||||
|
Q_PROPERTY(qreal magnitudeIntegrationTime READ getMagnitudeIntegrationTime WRITE setMagnitudeIntegrationTime
|
||||||
|
DESIGNABLE true)
|
||||||
|
|
||||||
|
// Duration is denoted in seconds.
|
||||||
|
Q_PROPERTY(qreal peakHoldDuration READ getPeakHoldDuration WRITE setPeakHoldDuration DESIGNABLE true)
|
||||||
|
Q_PROPERTY(qreal inputPeakHoldDuration READ getInputPeakHoldDuration WRITE setInputPeakHoldDuration
|
||||||
|
DESIGNABLE true)
|
||||||
|
|
||||||
|
friend class VolControl;
|
||||||
|
|
||||||
|
private:
|
||||||
|
obs_volmeter_t *obs_volmeter;
|
||||||
|
static std::weak_ptr<VolumeMeterTimer> updateTimer;
|
||||||
|
std::shared_ptr<VolumeMeterTimer> updateTimerRef;
|
||||||
|
|
||||||
|
inline void resetLevels();
|
||||||
|
inline void doLayout();
|
||||||
|
inline bool detectIdle(uint64_t ts);
|
||||||
|
inline void calculateBallistics(uint64_t ts, qreal timeSinceLastRedraw = 0.0);
|
||||||
|
inline void calculateBallisticsForChannel(int channelNr, uint64_t ts, qreal timeSinceLastRedraw);
|
||||||
|
|
||||||
|
inline int convertToInt(float number);
|
||||||
|
void paintInputMeter(QPainter &painter, int x, int y, int width, int height, float peakHold);
|
||||||
|
void paintHMeter(QPainter &painter, int x, int y, int width, int height, float magnitude, float peak,
|
||||||
|
float peakHold);
|
||||||
|
void paintHTicks(QPainter &painter, int x, int y, int width);
|
||||||
|
void paintVMeter(QPainter &painter, int x, int y, int width, int height, float magnitude, float peak,
|
||||||
|
float peakHold);
|
||||||
|
void paintVTicks(QPainter &painter, int x, int y, int height);
|
||||||
|
|
||||||
|
QMutex dataMutex;
|
||||||
|
|
||||||
|
bool recalculateLayout = true;
|
||||||
|
uint64_t currentLastUpdateTime = 0;
|
||||||
|
float currentMagnitude[MAX_AUDIO_CHANNELS];
|
||||||
|
float currentPeak[MAX_AUDIO_CHANNELS];
|
||||||
|
float currentInputPeak[MAX_AUDIO_CHANNELS];
|
||||||
|
|
||||||
|
int displayNrAudioChannels = 0;
|
||||||
|
float displayMagnitude[MAX_AUDIO_CHANNELS];
|
||||||
|
float displayPeak[MAX_AUDIO_CHANNELS];
|
||||||
|
float displayPeakHold[MAX_AUDIO_CHANNELS];
|
||||||
|
uint64_t displayPeakHoldLastUpdateTime[MAX_AUDIO_CHANNELS];
|
||||||
|
float displayInputPeakHold[MAX_AUDIO_CHANNELS];
|
||||||
|
uint64_t displayInputPeakHoldLastUpdateTime[MAX_AUDIO_CHANNELS];
|
||||||
|
|
||||||
|
QFont tickFont;
|
||||||
|
QColor backgroundNominalColor;
|
||||||
|
QColor backgroundWarningColor;
|
||||||
|
QColor backgroundErrorColor;
|
||||||
|
QColor foregroundNominalColor;
|
||||||
|
QColor foregroundWarningColor;
|
||||||
|
QColor foregroundErrorColor;
|
||||||
|
|
||||||
|
QColor backgroundNominalColorDisabled;
|
||||||
|
QColor backgroundWarningColorDisabled;
|
||||||
|
QColor backgroundErrorColorDisabled;
|
||||||
|
QColor foregroundNominalColorDisabled;
|
||||||
|
QColor foregroundWarningColorDisabled;
|
||||||
|
QColor foregroundErrorColorDisabled;
|
||||||
|
|
||||||
|
QColor clipColor;
|
||||||
|
QColor magnitudeColor;
|
||||||
|
QColor majorTickColor;
|
||||||
|
QColor minorTickColor;
|
||||||
|
|
||||||
|
int meterThickness;
|
||||||
|
qreal meterFontScaling;
|
||||||
|
|
||||||
|
qreal minimumLevel;
|
||||||
|
qreal warningLevel;
|
||||||
|
qreal errorLevel;
|
||||||
|
qreal clipLevel;
|
||||||
|
qreal minimumInputLevel;
|
||||||
|
qreal peakDecayRate;
|
||||||
|
qreal magnitudeIntegrationTime;
|
||||||
|
qreal peakHoldDuration;
|
||||||
|
qreal inputPeakHoldDuration;
|
||||||
|
|
||||||
|
QColor p_backgroundNominalColor;
|
||||||
|
QColor p_backgroundWarningColor;
|
||||||
|
QColor p_backgroundErrorColor;
|
||||||
|
QColor p_foregroundNominalColor;
|
||||||
|
QColor p_foregroundWarningColor;
|
||||||
|
QColor p_foregroundErrorColor;
|
||||||
|
|
||||||
|
uint64_t lastRedrawTime = 0;
|
||||||
|
int channels = 0;
|
||||||
|
bool clipping = false;
|
||||||
|
bool vertical;
|
||||||
|
bool muted = false;
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit VolumeMeter(QWidget *parent = nullptr, obs_volmeter_t *obs_volmeter = nullptr, bool vertical = false);
|
||||||
|
~VolumeMeter();
|
||||||
|
|
||||||
|
void setLevels(const float magnitude[MAX_AUDIO_CHANNELS], const float peak[MAX_AUDIO_CHANNELS],
|
||||||
|
const float inputPeak[MAX_AUDIO_CHANNELS]);
|
||||||
|
QRect getBarRect() const;
|
||||||
|
bool needLayoutChange();
|
||||||
|
|
||||||
|
QColor getBackgroundNominalColor() const;
|
||||||
|
void setBackgroundNominalColor(QColor c);
|
||||||
|
QColor getBackgroundWarningColor() const;
|
||||||
|
void setBackgroundWarningColor(QColor c);
|
||||||
|
QColor getBackgroundErrorColor() const;
|
||||||
|
void setBackgroundErrorColor(QColor c);
|
||||||
|
QColor getForegroundNominalColor() const;
|
||||||
|
void setForegroundNominalColor(QColor c);
|
||||||
|
QColor getForegroundWarningColor() const;
|
||||||
|
void setForegroundWarningColor(QColor c);
|
||||||
|
QColor getForegroundErrorColor() const;
|
||||||
|
void setForegroundErrorColor(QColor c);
|
||||||
|
|
||||||
|
QColor getBackgroundNominalColorDisabled() const;
|
||||||
|
void setBackgroundNominalColorDisabled(QColor c);
|
||||||
|
QColor getBackgroundWarningColorDisabled() const;
|
||||||
|
void setBackgroundWarningColorDisabled(QColor c);
|
||||||
|
QColor getBackgroundErrorColorDisabled() const;
|
||||||
|
void setBackgroundErrorColorDisabled(QColor c);
|
||||||
|
QColor getForegroundNominalColorDisabled() const;
|
||||||
|
void setForegroundNominalColorDisabled(QColor c);
|
||||||
|
QColor getForegroundWarningColorDisabled() const;
|
||||||
|
void setForegroundWarningColorDisabled(QColor c);
|
||||||
|
QColor getForegroundErrorColorDisabled() const;
|
||||||
|
void setForegroundErrorColorDisabled(QColor c);
|
||||||
|
|
||||||
|
QColor getClipColor() const;
|
||||||
|
void setClipColor(QColor c);
|
||||||
|
QColor getMagnitudeColor() const;
|
||||||
|
void setMagnitudeColor(QColor c);
|
||||||
|
QColor getMajorTickColor() const;
|
||||||
|
void setMajorTickColor(QColor c);
|
||||||
|
QColor getMinorTickColor() const;
|
||||||
|
void setMinorTickColor(QColor c);
|
||||||
|
int getMeterThickness() const;
|
||||||
|
void setMeterThickness(int v);
|
||||||
|
qreal getMeterFontScaling() const;
|
||||||
|
void setMeterFontScaling(qreal v);
|
||||||
|
qreal getMinimumLevel() const;
|
||||||
|
void setMinimumLevel(qreal v);
|
||||||
|
qreal getWarningLevel() const;
|
||||||
|
void setWarningLevel(qreal v);
|
||||||
|
qreal getErrorLevel() const;
|
||||||
|
void setErrorLevel(qreal v);
|
||||||
|
qreal getClipLevel() const;
|
||||||
|
void setClipLevel(qreal v);
|
||||||
|
qreal getMinimumInputLevel() const;
|
||||||
|
void setMinimumInputLevel(qreal v);
|
||||||
|
qreal getPeakDecayRate() const;
|
||||||
|
void setPeakDecayRate(qreal v);
|
||||||
|
qreal getMagnitudeIntegrationTime() const;
|
||||||
|
void setMagnitudeIntegrationTime(qreal v);
|
||||||
|
qreal getPeakHoldDuration() const;
|
||||||
|
void setPeakHoldDuration(qreal v);
|
||||||
|
qreal getInputPeakHoldDuration() const;
|
||||||
|
void setInputPeakHoldDuration(qreal v);
|
||||||
|
void setPeakMeterType(enum obs_peak_meter_type peakMeterType);
|
||||||
|
virtual void mousePressEvent(QMouseEvent *event) override;
|
||||||
|
virtual void wheelEvent(QWheelEvent *event) override;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void paintEvent(QPaintEvent *event) override;
|
||||||
|
void changeEvent(QEvent *e) override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class VolumeMeterTimer : public QTimer {
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
inline VolumeMeterTimer() : QTimer() {}
|
||||||
|
|
||||||
|
void AddVolControl(VolumeMeter *meter);
|
||||||
|
void RemoveVolControl(VolumeMeter *meter);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void timerEvent(QTimerEvent *event) override;
|
||||||
|
QList<VolumeMeter *> volumeMeters;
|
||||||
|
};
|
||||||
|
|
||||||
|
class QLabel;
|
||||||
|
class VolumeSlider;
|
||||||
|
class MuteCheckBox;
|
||||||
|
class OBSSourceLabel;
|
||||||
|
|
||||||
|
class VolControl : public QFrame {
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
private:
|
||||||
|
OBSSource source;
|
||||||
|
std::vector<OBSSignal> sigs;
|
||||||
|
OBSSourceLabel *nameLabel;
|
||||||
|
QLabel *volLabel;
|
||||||
|
VolumeMeter *volMeter;
|
||||||
|
VolumeSlider *slider;
|
||||||
|
MuteCheckBox *mute;
|
||||||
|
QPushButton *config = nullptr;
|
||||||
|
float levelTotal;
|
||||||
|
float levelCount;
|
||||||
|
OBSFader obs_fader;
|
||||||
|
OBSVolMeter obs_volmeter;
|
||||||
|
bool vertical;
|
||||||
|
QMenu *contextMenu;
|
||||||
|
|
||||||
|
static void OBSVolumeChanged(void *param, float db);
|
||||||
|
static void OBSVolumeLevel(void *data, const float magnitude[MAX_AUDIO_CHANNELS],
|
||||||
|
const float peak[MAX_AUDIO_CHANNELS], const float inputPeak[MAX_AUDIO_CHANNELS]);
|
||||||
|
static void OBSVolumeMuted(void *data, calldata_t *calldata);
|
||||||
|
static void OBSMixersOrMonitoringChanged(void *data, calldata_t *);
|
||||||
|
|
||||||
|
void EmitConfigClicked();
|
||||||
|
|
||||||
|
private slots:
|
||||||
|
void VolumeChanged();
|
||||||
|
void VolumeMuted(bool muted);
|
||||||
|
void MixersOrMonitoringChanged();
|
||||||
|
|
||||||
|
void SetMuted(bool checked);
|
||||||
|
void SliderChanged(int vol);
|
||||||
|
void updateText();
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void ConfigClicked();
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit VolControl(OBSSource source, bool showConfig = false, bool vertical = false);
|
||||||
|
~VolControl();
|
||||||
|
|
||||||
|
inline obs_source_t *GetSource() const { return source; }
|
||||||
|
|
||||||
|
void SetMeterDecayRate(qreal q);
|
||||||
|
void setPeakMeterType(enum obs_peak_meter_type peakMeterType);
|
||||||
|
|
||||||
|
void EnableSlider(bool enable);
|
||||||
|
inline void SetContextMenu(QMenu *cm) { contextMenu = cm; }
|
||||||
|
|
||||||
|
void refreshColors();
|
||||||
|
};
|
||||||
|
|
||||||
|
class VolumeSlider : public AbsoluteSlider {
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
obs_fader_t *fad;
|
||||||
|
|
||||||
|
VolumeSlider(obs_fader_t *fader, QWidget *parent = nullptr);
|
||||||
|
VolumeSlider(obs_fader_t *fader, Qt::Orientation orientation, QWidget *parent = nullptr);
|
||||||
|
|
||||||
|
bool getDisplayTicks() const;
|
||||||
|
void setDisplayTicks(bool display);
|
||||||
|
|
||||||
|
private:
|
||||||
|
bool displayTicks = false;
|
||||||
|
QColor tickColor;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
virtual void paintEvent(QPaintEvent *event) override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class VolumeAccessibleInterface : public QAccessibleWidget {
|
||||||
|
|
||||||
|
public:
|
||||||
|
VolumeAccessibleInterface(QWidget *w);
|
||||||
|
|
||||||
|
QVariant currentValue() const;
|
||||||
|
void setCurrentValue(const QVariant &value);
|
||||||
|
|
||||||
|
QVariant maximumValue() const;
|
||||||
|
QVariant minimumValue() const;
|
||||||
|
|
||||||
|
QVariant minimumStepSize() const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
VolumeSlider *slider() const;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
virtual QAccessible::Role role() const override;
|
||||||
|
virtual QString text(QAccessible::Text t) const override;
|
||||||
|
};
|
Loading…
x
Reference in New Issue
Block a user