mirror of
https://github.com/WarmUpTill/SceneSwitcher.git
synced 2026-03-22 01:44:49 -05:00
This was done to enable users to to use higher precision values in the "Audio" condition type. Previously, the UI would reset the selected values to the minimum value possible to display in the volume meter widget, which was -60 dB.
802 lines
22 KiB
C++
802 lines
22 KiB
C++
#include "macro-condition-audio.hpp"
|
|
#include "audio-helpers.hpp"
|
|
#include "layout-helpers.hpp"
|
|
#include "macro-helpers.hpp"
|
|
#include "selection-helpers.hpp"
|
|
|
|
namespace advss {
|
|
|
|
constexpr int64_t nsPerMs = 1000000;
|
|
const std::string MacroConditionAudio::id = "audio";
|
|
|
|
bool MacroConditionAudio::_registered = MacroConditionFactory::Register(
|
|
MacroConditionAudio::id,
|
|
{MacroConditionAudio::Create, MacroConditionAudioEdit::Create,
|
|
"AdvSceneSwitcher.condition.audio"});
|
|
|
|
const static std::map<MacroConditionAudio::Type, std::string> checkTypes = {
|
|
{MacroConditionAudio::Type::OUTPUT_VOLUME,
|
|
"AdvSceneSwitcher.condition.audio.type.output"},
|
|
{MacroConditionAudio::Type::CONFIGURED_VOLUME,
|
|
"AdvSceneSwitcher.condition.audio.type.volume"},
|
|
{MacroConditionAudio::Type::SYNC_OFFSET,
|
|
"AdvSceneSwitcher.condition.audio.type.syncOffset"},
|
|
{MacroConditionAudio::Type::MONITOR,
|
|
"AdvSceneSwitcher.condition.audio.type.monitor"},
|
|
{MacroConditionAudio::Type::BALANCE,
|
|
"AdvSceneSwitcher.condition.audio.type.balance"},
|
|
};
|
|
|
|
const static std::map<MacroConditionAudio::OutputCondition, std::string>
|
|
audioOutputConditionTypes = {
|
|
{MacroConditionAudio::OutputCondition::ABOVE,
|
|
"AdvSceneSwitcher.condition.audio.state.above"},
|
|
{MacroConditionAudio::OutputCondition::BELOW,
|
|
"AdvSceneSwitcher.condition.audio.state.below"},
|
|
};
|
|
|
|
const static std::map<MacroConditionAudio::VolumeCondition, std::string>
|
|
audioVolumeConditionTypes = {
|
|
{MacroConditionAudio::VolumeCondition::ABOVE,
|
|
"AdvSceneSwitcher.condition.audio.state.above"},
|
|
{MacroConditionAudio::VolumeCondition::EXACT,
|
|
"AdvSceneSwitcher.condition.audio.state.exact"},
|
|
{MacroConditionAudio::VolumeCondition::BELOW,
|
|
"AdvSceneSwitcher.condition.audio.state.below"},
|
|
{MacroConditionAudio::VolumeCondition::MUTE,
|
|
"AdvSceneSwitcher.condition.audio.state.mute"},
|
|
{MacroConditionAudio::VolumeCondition::UNMUTE,
|
|
"AdvSceneSwitcher.condition.audio.state.unmute"},
|
|
};
|
|
|
|
void MacroConditionAudio::SetType(const Type &type)
|
|
{
|
|
_checkType = type;
|
|
SetupTempVars();
|
|
}
|
|
|
|
MacroConditionAudio::~MacroConditionAudio()
|
|
{
|
|
obs_volmeter_remove_callback(_volmeter, SetVolumeLevel, this);
|
|
obs_volmeter_destroy(_volmeter);
|
|
}
|
|
|
|
float MacroConditionAudio::GetVolumePeak()
|
|
{
|
|
using namespace std::chrono_literals;
|
|
using namespace std::chrono;
|
|
static constexpr std::chrono::milliseconds timeout = 250ms;
|
|
|
|
// OBS might rarely not update _peak quickly enough when very low
|
|
// intervals are configured on the General tab.
|
|
// In that case _peak might be set to negative infinity still, which
|
|
// will result in unexpected behavior, so we use the previously valid
|
|
// peak value instead.
|
|
//
|
|
// If no volume update was received within a timeout window, however, it
|
|
// is assumed, that the source no longer produces any audio output and
|
|
// thus a peak volume value of negative infinity is used.
|
|
|
|
float peak;
|
|
|
|
std::lock_guard<std::mutex> lock(_peakMutex);
|
|
auto msPassedSinceLastUpdate = duration_cast<milliseconds>(
|
|
high_resolution_clock::now() - _lastPeakUpdate);
|
|
if (_lastPeakUpdate.time_since_epoch().count() != 0 &&
|
|
msPassedSinceLastUpdate > timeout) {
|
|
peak = -std::numeric_limits<float>::infinity();
|
|
} else {
|
|
peak = _peakUpdated ? _peak : _previousPeak;
|
|
}
|
|
|
|
_previousPeak = peak;
|
|
_peak = -std::numeric_limits<float>::infinity();
|
|
_peakUpdated = false;
|
|
|
|
return peak;
|
|
}
|
|
|
|
bool MacroConditionAudio::CheckOutputCondition()
|
|
{
|
|
bool ret = false;
|
|
OBSSourceAutoRelease source =
|
|
obs_weak_source_get_source(_audioSource.GetSource());
|
|
|
|
float peak = GetVolumePeak();
|
|
double curVolume = _useDb ? peak : DecibelToPercent(peak) * 100;
|
|
|
|
switch (_outputCondition) {
|
|
case OutputCondition::ABOVE:
|
|
if (_useDb) {
|
|
ret = curVolume > _volumeDB;
|
|
} else {
|
|
ret = curVolume > _volumePercent;
|
|
}
|
|
break;
|
|
case OutputCondition::BELOW:
|
|
if (_useDb) {
|
|
ret = curVolume < _volumeDB;
|
|
} else {
|
|
ret = curVolume < _volumePercent;
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
SetVariableValue(std::to_string(curVolume));
|
|
SetTempVarValue("output_volume", std::to_string(curVolume));
|
|
|
|
// Reset for next check
|
|
if (_audioSource.GetType() == SourceSelection::Type::VARIABLE) {
|
|
ResetVolmeter();
|
|
}
|
|
|
|
return ret && source;
|
|
}
|
|
|
|
bool MacroConditionAudio::CheckVolumeCondition()
|
|
{
|
|
bool ret = false;
|
|
OBSSourceAutoRelease source =
|
|
obs_weak_source_get_source(_audioSource.GetSource());
|
|
|
|
float curVolume =
|
|
_useDb ? PercentToDecibel(obs_source_get_volume(source))
|
|
: obs_source_get_volume(source);
|
|
bool muted = obs_source_muted(source);
|
|
|
|
switch (_volumeCondition) {
|
|
case VolumeCondition::ABOVE:
|
|
if (_useDb) {
|
|
ret = curVolume > _volumeDB;
|
|
} else {
|
|
ret = curVolume > _volumePercent / 100.f;
|
|
}
|
|
SetVariableValue(std::to_string(curVolume));
|
|
break;
|
|
case VolumeCondition::EXACT:
|
|
if (_useDb) {
|
|
ret = curVolume == _volumeDB;
|
|
} else {
|
|
ret = curVolume == _volumePercent / 100.f;
|
|
}
|
|
SetVariableValue(std::to_string(curVolume));
|
|
break;
|
|
case VolumeCondition::BELOW:
|
|
if (_useDb) {
|
|
ret = curVolume < _volumeDB;
|
|
} else {
|
|
ret = curVolume < _volumePercent / 100.f;
|
|
}
|
|
SetVariableValue(std::to_string(curVolume));
|
|
break;
|
|
case VolumeCondition::MUTE:
|
|
ret = muted;
|
|
SetVariableValue("");
|
|
break;
|
|
case VolumeCondition::UNMUTE:
|
|
ret = !muted;
|
|
SetVariableValue("");
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
SetTempVarValue("configured_volume", std::to_string(curVolume));
|
|
SetTempVarValue("muted", (source && muted) ? "true" : "false");
|
|
|
|
return ret && source;
|
|
}
|
|
|
|
bool MacroConditionAudio::CheckSyncOffset()
|
|
{
|
|
if (!_audioSource.GetSource()) {
|
|
return false;
|
|
}
|
|
|
|
bool ret = false;
|
|
OBSSourceAutoRelease source =
|
|
obs_weak_source_get_source(_audioSource.GetSource());
|
|
auto curOffset = obs_source_get_sync_offset(source) / nsPerMs;
|
|
if (_outputCondition == OutputCondition::ABOVE) {
|
|
ret = curOffset > _syncOffset;
|
|
} else {
|
|
ret = curOffset < _syncOffset;
|
|
}
|
|
SetVariableValue(std::to_string(curOffset));
|
|
SetTempVarValue("sync_offset", std::to_string(curOffset));
|
|
return ret && source;
|
|
}
|
|
|
|
bool MacroConditionAudio::CheckMonitor()
|
|
{
|
|
if (!_audioSource.GetSource()) {
|
|
return false;
|
|
}
|
|
|
|
bool ret = false;
|
|
OBSSourceAutoRelease source =
|
|
obs_weak_source_get_source(_audioSource.GetSource());
|
|
ret = obs_source_get_monitoring_type(source) == _monitorType;
|
|
SetVariableValue("");
|
|
SetTempVarValue("monitor",
|
|
std::to_string(obs_source_get_monitoring_type(source)));
|
|
return ret && source;
|
|
}
|
|
|
|
bool MacroConditionAudio::CheckBalance()
|
|
{
|
|
if (!_audioSource.GetSource()) {
|
|
return false;
|
|
}
|
|
|
|
bool ret = false;
|
|
OBSSourceAutoRelease source =
|
|
obs_weak_source_get_source(_audioSource.GetSource());
|
|
auto curBalance = obs_source_get_balance_value(source);
|
|
if (_outputCondition == OutputCondition::ABOVE) {
|
|
ret = curBalance > _balance;
|
|
} else {
|
|
ret = curBalance < _balance;
|
|
}
|
|
SetVariableValue(std::to_string(curBalance));
|
|
SetTempVarValue("balance", std::to_string(curBalance));
|
|
return ret && source;
|
|
}
|
|
|
|
bool MacroConditionAudio::CheckCondition()
|
|
{
|
|
bool ret = false;
|
|
switch (_checkType) {
|
|
case Type::OUTPUT_VOLUME:
|
|
ret = CheckOutputCondition();
|
|
break;
|
|
case Type::CONFIGURED_VOLUME:
|
|
ret = CheckVolumeCondition();
|
|
break;
|
|
case Type::SYNC_OFFSET:
|
|
ret = CheckSyncOffset();
|
|
break;
|
|
case Type::MONITOR:
|
|
ret = CheckMonitor();
|
|
break;
|
|
case Type::BALANCE:
|
|
ret = CheckBalance();
|
|
break;
|
|
}
|
|
|
|
if (GetVariableValue().empty()) {
|
|
SetVariableValue(ret ? "true" : "false");
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
bool MacroConditionAudio::Save(obs_data_t *obj) const
|
|
{
|
|
MacroCondition::Save(obj);
|
|
_audioSource.Save(obj, "audioSource");
|
|
obs_data_set_int(obj, "monitor", _monitorType);
|
|
_volumePercent.Save(obj, "volume");
|
|
_syncOffset.Save(obj, "syncOffset");
|
|
_balance.Save(obj, "balance");
|
|
obs_data_set_int(obj, "checkType", static_cast<int>(_checkType));
|
|
obs_data_set_int(obj, "outputCondition",
|
|
static_cast<int>(_outputCondition));
|
|
obs_data_set_int(obj, "volumeCondition",
|
|
static_cast<int>(_volumeCondition));
|
|
obs_data_set_bool(obj, "useDb", _useDb);
|
|
_volumeDB.Save(obj, "volumeDB");
|
|
obs_data_set_int(obj, "version", 3);
|
|
return true;
|
|
}
|
|
|
|
obs_volmeter_t *AddVolmeterToSource(MacroConditionAudio *entry,
|
|
obs_weak_source *source)
|
|
{
|
|
obs_volmeter_t *volmeter = obs_volmeter_create(OBS_FADER_LOG);
|
|
obs_volmeter_add_callback(volmeter, MacroConditionAudio::SetVolumeLevel,
|
|
entry);
|
|
OBSSourceAutoRelease audioSource = obs_weak_source_get_source(source);
|
|
if (!obs_volmeter_attach_source(volmeter, audioSource)) {
|
|
const char *name = obs_source_get_name(audioSource);
|
|
blog(LOG_WARNING, "failed to attach volmeter to source %s",
|
|
name);
|
|
}
|
|
|
|
return volmeter;
|
|
}
|
|
|
|
bool MacroConditionAudio::Load(obs_data_t *obj)
|
|
{
|
|
MacroCondition::Load(obj);
|
|
_audioSource.Load(obj, "audioSource");
|
|
_monitorType = static_cast<obs_monitoring_type>(
|
|
obs_data_get_int(obj, "monitor"));
|
|
if (!obs_data_has_user_value(obj, "version")) {
|
|
_volumePercent = obs_data_get_int(obj, "volume");
|
|
_syncOffset = obs_data_get_int(obj, "syncOffset");
|
|
_balance = obs_data_get_double(obj, "balance");
|
|
} else {
|
|
_volumePercent.Load(obj, "volume");
|
|
_syncOffset.Load(obj, "syncOffset");
|
|
_balance.Load(obj, "balance");
|
|
}
|
|
_checkType = static_cast<Type>(obs_data_get_int(obj, "checkType"));
|
|
_outputCondition = static_cast<OutputCondition>(
|
|
obs_data_get_int(obj, "outputCondition"));
|
|
_volumeCondition = static_cast<VolumeCondition>(
|
|
obs_data_get_int(obj, "volumeCondition"));
|
|
_volmeter = AddVolmeterToSource(this, _audioSource.GetSource());
|
|
|
|
if (obs_data_get_int(obj, "version") < 2) {
|
|
// Set default values for dB handling
|
|
_useDb = false;
|
|
_volumeDB = 0.0;
|
|
|
|
// In previous versions the scaling happened in CheckCondition()
|
|
// so these values must now be converted to enable compatibility
|
|
// with the decibel value based checks.
|
|
OBSDataAutoRelease data = obs_data_get_obj(obj, "volume");
|
|
auto oldFixedValue = obs_data_get_int(data, "value");
|
|
_volumeDB = ((double)oldFixedValue / 1.7) - 60;
|
|
_volumePercent = DecibelToPercent(_volumeDB) * 100;
|
|
} else {
|
|
_useDb = obs_data_get_bool(obj, "useDb");
|
|
_volumeDB.Load(obj, "volumeDB");
|
|
}
|
|
return true;
|
|
}
|
|
|
|
std::string MacroConditionAudio::GetShortDesc() const
|
|
{
|
|
return _audioSource.ToString();
|
|
}
|
|
|
|
void MacroConditionAudio::SetVolumeLevel(void *data, const float *,
|
|
const float peak[MAX_AUDIO_CHANNELS],
|
|
const float *)
|
|
{
|
|
auto c = static_cast<MacroConditionAudio *>(data);
|
|
const auto macro = c->GetMacro();
|
|
if (MacroIsPaused(macro)) {
|
|
return;
|
|
}
|
|
|
|
std::lock_guard<std::mutex> lock(c->_peakMutex);
|
|
for (int i = 0; i < MAX_AUDIO_CHANNELS; i++) {
|
|
if (peak[i] > c->_peak) {
|
|
c->_peak = peak[i];
|
|
}
|
|
}
|
|
c->_peakUpdated = true;
|
|
c->_lastPeakUpdate = std::chrono::high_resolution_clock::now();
|
|
}
|
|
|
|
void MacroConditionAudio::ResetVolmeter()
|
|
{
|
|
obs_volmeter_remove_callback(_volmeter, SetVolumeLevel, this);
|
|
obs_volmeter_destroy(_volmeter);
|
|
|
|
_volmeter = AddVolmeterToSource(this, _audioSource.GetSource());
|
|
}
|
|
|
|
void MacroConditionAudio::SetupTempVars()
|
|
{
|
|
MacroCondition::SetupTempVars();
|
|
switch (_checkType) {
|
|
case Type::OUTPUT_VOLUME:
|
|
AddTempvar(
|
|
"output_volume",
|
|
obs_module_text(
|
|
"AdvSceneSwitcher.tempVar.audio.output_volume"),
|
|
obs_module_text(
|
|
"AdvSceneSwitcher.tempVar.audio.output_volume.description"));
|
|
break;
|
|
case Type::CONFIGURED_VOLUME:
|
|
AddTempvar(
|
|
"configured_volume",
|
|
obs_module_text(
|
|
"AdvSceneSwitcher.tempVar.audio.configured_volume"),
|
|
obs_module_text(
|
|
"AdvSceneSwitcher.tempVar.audio.configured_volume.description"));
|
|
AddTempvar("muted",
|
|
obs_module_text(
|
|
"AdvSceneSwitcher.tempVar.audio.muted"));
|
|
break;
|
|
case Type::SYNC_OFFSET:
|
|
AddTempvar(
|
|
"sync_offset",
|
|
obs_module_text(
|
|
"AdvSceneSwitcher.tempVar.audio.sync_offset"));
|
|
break;
|
|
case Type::MONITOR:
|
|
AddTempvar("monitor",
|
|
obs_module_text(
|
|
"AdvSceneSwitcher.tempVar.audio.monitor"));
|
|
break;
|
|
case Type::BALANCE:
|
|
AddTempvar("balance",
|
|
obs_module_text(
|
|
"AdvSceneSwitcher.tempVar.audio.balance"));
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
static inline void populateCheckTypes(QComboBox *list)
|
|
{
|
|
list->clear();
|
|
for (const auto &[type, name] : checkTypes) {
|
|
if (type == MacroConditionAudio::Type::MONITOR) {
|
|
if (obs_audio_monitoring_available()) {
|
|
list->addItem(obs_module_text(name.c_str()),
|
|
static_cast<int>(type));
|
|
}
|
|
} else {
|
|
list->addItem(obs_module_text(name.c_str()),
|
|
static_cast<int>(type));
|
|
}
|
|
}
|
|
}
|
|
|
|
static inline void populateOutputConditionSelection(QComboBox *list)
|
|
{
|
|
list->clear();
|
|
for (const auto &[_, name] : audioOutputConditionTypes) {
|
|
list->addItem(obs_module_text(name.c_str()));
|
|
}
|
|
}
|
|
|
|
static inline void populateVolumeConditionSelection(QComboBox *list)
|
|
{
|
|
list->clear();
|
|
for (const auto &[_, name] : audioVolumeConditionTypes) {
|
|
list->addItem(obs_module_text(name.c_str()));
|
|
}
|
|
}
|
|
|
|
MacroConditionAudioEdit::MacroConditionAudioEdit(
|
|
QWidget *parent, std::shared_ptr<MacroConditionAudio> entryData)
|
|
: QWidget(parent),
|
|
_checkTypes(new QComboBox()),
|
|
_sources(new SourceSelectionWidget(this, QStringList(), true)),
|
|
_condition(new QComboBox()),
|
|
_volumePercent(new VariableDoubleSpinBox()),
|
|
_volumeDB(new VariableDoubleSpinBox),
|
|
_percentDBToggle(new QPushButton),
|
|
_syncOffset(new VariableSpinBox()),
|
|
_monitorTypes(new QComboBox),
|
|
_balance(new SliderSpinBox(0., 1., ""))
|
|
{
|
|
_volumePercent->setSuffix("%");
|
|
_volumePercent->setMaximum(100);
|
|
_volumePercent->setMinimum(0);
|
|
|
|
_volumeDB->setMinimum(-100);
|
|
_volumeDB->setMaximum(0);
|
|
_volumeDB->setSuffix("dB");
|
|
_volumeDB->specialValueText("-inf");
|
|
|
|
_syncOffset->setMinimum(-950);
|
|
_syncOffset->setMaximum(20000);
|
|
_syncOffset->setSuffix("ms");
|
|
|
|
auto sources = GetAudioSourceNames();
|
|
sources.sort();
|
|
_sources->SetSourceNameList(sources);
|
|
|
|
QWidget::connect(_checkTypes, SIGNAL(currentIndexChanged(int)), this,
|
|
SLOT(CheckTypeChanged(int)));
|
|
QWidget::connect(
|
|
_volumePercent,
|
|
SIGNAL(NumberVariableChanged(const NumberVariable<double> &)),
|
|
this,
|
|
SLOT(VolumePercentChanged(const NumberVariable<double> &)));
|
|
QWidget::connect(
|
|
_syncOffset,
|
|
SIGNAL(NumberVariableChanged(const NumberVariable<int> &)),
|
|
this, SLOT(SyncOffsetChanged(const NumberVariable<int> &)));
|
|
QWidget::connect(_monitorTypes, SIGNAL(currentIndexChanged(int)), this,
|
|
SLOT(MonitorTypeChanged(int)));
|
|
QWidget::connect(
|
|
_balance,
|
|
SIGNAL(DoubleValueChanged(const NumberVariable<double> &)),
|
|
this, SLOT(BalanceChanged(const NumberVariable<double> &)));
|
|
QWidget::connect(_condition, SIGNAL(currentIndexChanged(int)), this,
|
|
SLOT(ConditionChanged(int)));
|
|
QWidget::connect(_sources,
|
|
SIGNAL(SourceChanged(const SourceSelection &)), this,
|
|
SLOT(SourceChanged(const SourceSelection &)));
|
|
QWidget::connect(
|
|
_volumeDB,
|
|
SIGNAL(NumberVariableChanged(const NumberVariable<double> &)),
|
|
this, SLOT(VolumeDBChanged(const NumberVariable<double> &)));
|
|
QWidget::connect(_percentDBToggle, SIGNAL(clicked()), this,
|
|
SLOT(PercentDBClicked()));
|
|
|
|
populateCheckTypes(_checkTypes);
|
|
PopulateMonitorTypeSelection(_monitorTypes);
|
|
|
|
QHBoxLayout *switchLayout = new QHBoxLayout;
|
|
std::unordered_map<std::string, QWidget *> widgetPlaceholders = {
|
|
{"{{checkType}}", _checkTypes},
|
|
{"{{audioSources}}", _sources},
|
|
{"{{volume}}", _volumePercent},
|
|
{"{{syncOffset}}", _syncOffset},
|
|
{"{{monitorTypes}}", _monitorTypes},
|
|
{"{{balance}}", _balance},
|
|
{"{{condition}}", _condition},
|
|
{"{{volumeDB}}", _volumeDB},
|
|
{"{{percentDBToggle}}", _percentDBToggle},
|
|
};
|
|
PlaceWidgets(obs_module_text("AdvSceneSwitcher.condition.audio.entry"),
|
|
switchLayout, widgetPlaceholders);
|
|
|
|
QVBoxLayout *mainLayout = new QVBoxLayout;
|
|
mainLayout->addLayout(switchLayout);
|
|
mainLayout->addWidget(_balance);
|
|
setLayout(mainLayout);
|
|
|
|
_entryData = entryData;
|
|
UpdateEntryData();
|
|
_loading = false;
|
|
}
|
|
|
|
void MacroConditionAudioEdit::UpdateVolmeterSource()
|
|
{
|
|
delete _volMeter;
|
|
OBSSourceAutoRelease soruce = obs_weak_source_get_source(
|
|
_entryData->_audioSource.GetSource());
|
|
_volMeter = new VolControl(soruce.Get());
|
|
|
|
auto layout = this->layout();
|
|
layout->addWidget(_volMeter);
|
|
|
|
QWidget::connect(_volMeter->GetSlider(),
|
|
&DoubleSlider::DoubleValChanged,
|
|
[=](double) { SyncSliderAndValueSelection(true); });
|
|
|
|
// Slider will default to 0 so set it manually once
|
|
SyncSliderAndValueSelection(false);
|
|
}
|
|
|
|
void MacroConditionAudioEdit::SourceChanged(const SourceSelection &source)
|
|
{
|
|
if (_loading || !_entryData) {
|
|
return;
|
|
}
|
|
|
|
{
|
|
auto lock = LockContext();
|
|
_entryData->_audioSource = source;
|
|
_entryData->ResetVolmeter();
|
|
}
|
|
UpdateVolmeterSource();
|
|
SetWidgetVisibility();
|
|
emit HeaderInfoChanged(
|
|
QString::fromStdString(_entryData->GetShortDesc()));
|
|
}
|
|
|
|
void MacroConditionAudioEdit::VolumePercentChanged(
|
|
const NumberVariable<double> &vol)
|
|
{
|
|
if (_loading || !_entryData) {
|
|
return;
|
|
}
|
|
{
|
|
auto lock = LockContext();
|
|
_entryData->_volumePercent = vol;
|
|
}
|
|
|
|
SyncSliderAndValueSelection(false);
|
|
}
|
|
|
|
void MacroConditionAudioEdit::SyncOffsetChanged(const NumberVariable<int> &value)
|
|
{
|
|
if (_loading || !_entryData) {
|
|
return;
|
|
}
|
|
|
|
auto lock = LockContext();
|
|
_entryData->_syncOffset = value;
|
|
}
|
|
|
|
void MacroConditionAudioEdit::MonitorTypeChanged(int value)
|
|
{
|
|
if (_loading || !_entryData) {
|
|
return;
|
|
}
|
|
|
|
auto lock = LockContext();
|
|
_entryData->_monitorType = static_cast<obs_monitoring_type>(value);
|
|
}
|
|
|
|
void MacroConditionAudioEdit::BalanceChanged(const NumberVariable<double> &value)
|
|
{
|
|
if (_loading || !_entryData) {
|
|
return;
|
|
}
|
|
|
|
auto lock = LockContext();
|
|
_entryData->_balance = value;
|
|
}
|
|
|
|
void MacroConditionAudioEdit::VolumeDBChanged(
|
|
const NumberVariable<double> &value)
|
|
{
|
|
if (_loading || !_entryData) {
|
|
return;
|
|
}
|
|
{
|
|
auto lock = LockContext();
|
|
_entryData->_volumeDB = value;
|
|
}
|
|
SyncSliderAndValueSelection(false);
|
|
}
|
|
|
|
void MacroConditionAudioEdit::PercentDBClicked()
|
|
{
|
|
if (_loading || !_entryData) {
|
|
return;
|
|
}
|
|
|
|
auto lock = LockContext();
|
|
_entryData->_useDb = !_entryData->_useDb;
|
|
SetWidgetVisibility();
|
|
}
|
|
|
|
void MacroConditionAudioEdit::SyncSliderAndValueSelection(bool sliderMoved)
|
|
{
|
|
if (!_entryData) {
|
|
return;
|
|
}
|
|
|
|
if (sliderMoved) {
|
|
auto sliderPosition = _volMeter->GetSlider()->DoubleValue();
|
|
// Adjust to the dB scale on the volume meter widget
|
|
auto dBScaleValue = sliderPosition - 100.0;
|
|
|
|
if (_entryData->_useDb) {
|
|
_volumeDB->SetFixedValue(dBScaleValue);
|
|
const QSignalBlocker blocker(this);
|
|
_volumePercent->SetFixedValue(
|
|
DecibelToPercent(dBScaleValue) * 100);
|
|
} else {
|
|
_volumePercent->SetFixedValue(
|
|
DecibelToPercent(dBScaleValue) * 100);
|
|
const QSignalBlocker blocker(this);
|
|
_volumeDB->SetFixedValue(dBScaleValue);
|
|
}
|
|
} else {
|
|
const QSignalBlocker blocker(this);
|
|
double dBValue =
|
|
_entryData->_useDb
|
|
? _entryData->_volumeDB.GetFixedValue()
|
|
: PercentToDecibel(_entryData->_volumePercent /
|
|
100.0);
|
|
auto sliderPosition = (dBValue + 100);
|
|
_volMeter->GetSlider()->SetDoubleVal(sliderPosition);
|
|
}
|
|
}
|
|
|
|
void MacroConditionAudioEdit::ConditionChanged(int cond)
|
|
{
|
|
if (_loading || !_entryData) {
|
|
return;
|
|
}
|
|
|
|
auto lock = LockContext();
|
|
if (_entryData->GetType() == MacroConditionAudio::Type::OUTPUT_VOLUME ||
|
|
_entryData->GetType() == MacroConditionAudio::Type::BALANCE ||
|
|
_entryData->GetType() == MacroConditionAudio::Type::SYNC_OFFSET) {
|
|
_entryData->_outputCondition =
|
|
static_cast<MacroConditionAudio::OutputCondition>(cond);
|
|
} else {
|
|
_entryData->_volumeCondition =
|
|
static_cast<MacroConditionAudio::VolumeCondition>(cond);
|
|
}
|
|
SetWidgetVisibility();
|
|
}
|
|
|
|
void MacroConditionAudioEdit::CheckTypeChanged(int idx)
|
|
{
|
|
if (_loading || !_entryData) {
|
|
return;
|
|
}
|
|
|
|
auto lock = LockContext();
|
|
_entryData->SetType(static_cast<MacroConditionAudio::Type>(
|
|
_checkTypes->itemData(idx).toInt()));
|
|
|
|
const QSignalBlocker b(_condition);
|
|
if (_entryData->GetType() == MacroConditionAudio::Type::OUTPUT_VOLUME ||
|
|
_entryData->GetType() == MacroConditionAudio::Type::BALANCE ||
|
|
_entryData->GetType() == MacroConditionAudio::Type::SYNC_OFFSET) {
|
|
populateOutputConditionSelection(_condition);
|
|
} else if (_entryData->GetType() ==
|
|
MacroConditionAudio::Type::CONFIGURED_VOLUME) {
|
|
populateVolumeConditionSelection(_condition);
|
|
}
|
|
SetWidgetVisibility();
|
|
}
|
|
|
|
void MacroConditionAudioEdit::UpdateEntryData()
|
|
{
|
|
if (!_entryData) {
|
|
return;
|
|
}
|
|
|
|
_sources->SetSource(_entryData->_audioSource);
|
|
_volumePercent->SetValue(_entryData->_volumePercent);
|
|
_volumeDB->SetValue(_entryData->_volumeDB);
|
|
_syncOffset->SetValue(_entryData->_syncOffset);
|
|
_monitorTypes->setCurrentIndex(_entryData->_monitorType);
|
|
_balance->SetDoubleValue(_entryData->_balance);
|
|
_checkTypes->setCurrentIndex(
|
|
_checkTypes->findData(static_cast<int>(_entryData->GetType())));
|
|
|
|
if (_entryData->GetType() == MacroConditionAudio::Type::OUTPUT_VOLUME ||
|
|
_entryData->GetType() == MacroConditionAudio::Type::BALANCE ||
|
|
_entryData->GetType() == MacroConditionAudio::Type::SYNC_OFFSET) {
|
|
populateOutputConditionSelection(_condition);
|
|
_condition->setCurrentIndex(
|
|
static_cast<int>(_entryData->_outputCondition));
|
|
} else if (_entryData->GetType() ==
|
|
MacroConditionAudio::Type::CONFIGURED_VOLUME) {
|
|
populateVolumeConditionSelection(_condition);
|
|
_condition->setCurrentIndex(
|
|
static_cast<int>(_entryData->_volumeCondition));
|
|
}
|
|
|
|
UpdateVolmeterSource();
|
|
SetWidgetVisibility();
|
|
}
|
|
|
|
bool MacroConditionAudioEdit::HasVolumeControl() const
|
|
{
|
|
return _entryData->GetType() ==
|
|
MacroConditionAudio::Type::OUTPUT_VOLUME ||
|
|
(_entryData->GetType() ==
|
|
MacroConditionAudio::Type::CONFIGURED_VOLUME &&
|
|
(_entryData->_volumeCondition ==
|
|
MacroConditionAudio::VolumeCondition::ABOVE ||
|
|
_entryData->_volumeCondition ==
|
|
MacroConditionAudio::VolumeCondition::EXACT ||
|
|
_entryData->_volumeCondition ==
|
|
MacroConditionAudio::VolumeCondition::BELOW));
|
|
}
|
|
|
|
void MacroConditionAudioEdit::SetWidgetVisibility()
|
|
{
|
|
if (!_entryData) {
|
|
return;
|
|
}
|
|
|
|
_condition->setVisible(
|
|
_entryData->GetType() ==
|
|
MacroConditionAudio::Type::OUTPUT_VOLUME ||
|
|
_entryData->GetType() ==
|
|
MacroConditionAudio::Type::CONFIGURED_VOLUME ||
|
|
_entryData->GetType() == MacroConditionAudio::Type::BALANCE ||
|
|
_entryData->GetType() ==
|
|
MacroConditionAudio::Type::SYNC_OFFSET);
|
|
_syncOffset->setVisible(_entryData->GetType() ==
|
|
MacroConditionAudio::Type::SYNC_OFFSET);
|
|
_monitorTypes->setVisible(_entryData->GetType() ==
|
|
MacroConditionAudio::Type::MONITOR);
|
|
_balance->setVisible(_entryData->GetType() ==
|
|
MacroConditionAudio::Type::BALANCE);
|
|
_volMeter->setVisible(_entryData->GetType() ==
|
|
MacroConditionAudio::Type::OUTPUT_VOLUME);
|
|
_volumePercent->setVisible(HasVolumeControl() && !_entryData->_useDb);
|
|
_volumeDB->setVisible(HasVolumeControl() && _entryData->_useDb);
|
|
_percentDBToggle->setText(_entryData->_useDb ? "dB" : "%");
|
|
_percentDBToggle->setVisible(HasVolumeControl());
|
|
adjustSize();
|
|
}
|
|
|
|
} // namespace advss
|