Compare commits

...

6 Commits

Author SHA1 Message Date
WarmUpTill
70dad8ebb4 Handle MIDI device disconnect and reconnect 2024-03-09 12:00:52 +01:00
WarmUpTill
497b3b3e04 Clean up macro import and add action queues 2024-03-09 12:00:52 +01:00
WarmUpTill
891811aa47 Convert audio volume thresholds to new format 2024-03-09 09:18:29 +01:00
WarmUpTill
be9ddaa4c9 Increase precision of percent based volume threshold input 2024-03-09 09:18:29 +01:00
WarmUpTill
03494c2915 Adapt to volume widget slider changes 2024-03-09 09:18:29 +01:00
WarmUpTill
bcf7c247bf Increase precision of volume widget slider 2024-03-09 09:18:29 +01:00
16 changed files with 336 additions and 86 deletions

View File

@@ -151,6 +151,8 @@ target_sources(
lib/utils/backup.hpp lib/utils/backup.hpp
lib/utils/curl-helper.cpp lib/utils/curl-helper.cpp
lib/utils/curl-helper.hpp lib/utils/curl-helper.hpp
lib/utils/double-slider.cpp
lib/utils/double-slider.hpp
lib/utils/duration-control.cpp lib/utils/duration-control.cpp
lib/utils/duration-control.hpp lib/utils/duration-control.hpp
lib/utils/duration.cpp lib/utils/duration.cpp

View File

@@ -437,10 +437,8 @@ AudioSwitchWidget::AudioSwitchWidget(QWidget *parent, AudioSwitch *s)
audioVolumeThreshold->setMaximum(100); audioVolumeThreshold->setMaximum(100);
audioVolumeThreshold->setMinimum(0); audioVolumeThreshold->setMinimum(0);
QWidget::connect(volMeter->GetSlider(), SIGNAL(valueChanged(int)), QWidget::connect(volMeter->GetSlider(), &DoubleSlider::DoubleValChanged,
audioVolumeThreshold, SLOT(setValue(int))); [=](double) { SyncSliderAndValueSelection(true); });
QWidget::connect(audioVolumeThreshold, SIGNAL(valueChanged(int)),
volMeter->GetSlider(), SLOT(setValue(int)));
QWidget::connect(audioVolumeThreshold, SIGNAL(valueChanged(int)), this, QWidget::connect(audioVolumeThreshold, SIGNAL(valueChanged(int)), this,
SLOT(VolumeThresholdChanged(int))); SLOT(VolumeThresholdChanged(int)));
QWidget::connect(condition, SIGNAL(currentIndexChanged(int)), this, QWidget::connect(condition, SIGNAL(currentIndexChanged(int)), this,
@@ -487,6 +485,7 @@ AudioSwitchWidget::AudioSwitchWidget(QWidget *parent, AudioSwitch *s)
switchData = s; switchData = s;
loading = false; loading = false;
SyncSliderAndValueSelection(false);
} }
AudioSwitch *AudioSwitchWidget::getSwitchData() AudioSwitch *AudioSwitchWidget::getSwitchData()
@@ -509,6 +508,21 @@ void AudioSwitchWidget::swapSwitchData(AudioSwitchWidget *s1,
s2->setSwitchData(t); s2->setSwitchData(t);
} }
void AudioSwitchWidget::SyncSliderAndValueSelection(bool sliderMoved)
{
if (loading || !switchData) {
return;
}
if (sliderMoved) {
auto sliderPosition = volMeter->GetSlider()->DoubleValue();
audioVolumeThreshold->setValue(sliderPosition);
} else {
volMeter->GetSlider()->SetDoubleVal(
switchData->volumeThreshold);
}
}
void AudioSwitchWidget::UpdateVolmeterSource() void AudioSwitchWidget::UpdateVolmeterSource()
{ {
delete volMeter; delete volMeter;
@@ -520,10 +534,8 @@ void AudioSwitchWidget::UpdateVolmeterSource()
QLayout *layout = this->layout(); QLayout *layout = this->layout();
layout->addWidget(volMeter); layout->addWidget(volMeter);
QWidget::connect(volMeter->GetSlider(), SIGNAL(valueChanged(int)), QWidget::connect(volMeter->GetSlider(), &DoubleSlider::DoubleValChanged,
audioVolumeThreshold, SLOT(setValue(int))); [=](double) { SyncSliderAndValueSelection(true); });
QWidget::connect(audioVolumeThreshold, SIGNAL(valueChanged(int)),
volMeter->GetSlider(), SLOT(setValue(int)));
// Slider will default to 0 so set it manually once // Slider will default to 0 so set it manually once
volMeter->GetSlider()->setValue(switchData->volumeThreshold); volMeter->GetSlider()->setValue(switchData->volumeThreshold);
@@ -547,8 +559,11 @@ void AudioSwitchWidget::VolumeThresholdChanged(int vol)
return; return;
} }
std::lock_guard<std::mutex> lock(switcher->m); {
switchData->volumeThreshold = vol; std::lock_guard<std::mutex> lock(switcher->m);
switchData->volumeThreshold = vol;
}
SyncSliderAndValueSelection(false);
} }
void AudioSwitchWidget::ConditionChanged(int cond) void AudioSwitchWidget::ConditionChanged(int cond)

View File

@@ -79,6 +79,7 @@ private slots:
void ConditionChanged(int cond); void ConditionChanged(int cond);
void DurationChanged(const Duration &); void DurationChanged(const Duration &);
void IgnoreInactiveChanged(int state); void IgnoreInactiveChanged(int state);
void SyncSliderAndValueSelection(bool sliderMoved);
private: private:
QComboBox *audioSources; QComboBox *audioSources;

View File

@@ -1,4 +1,5 @@
#include "advanced-scene-switcher.hpp" #include "advanced-scene-switcher.hpp"
#include "action-queue.hpp"
#include "macro-action-edit.hpp" #include "macro-action-edit.hpp"
#include "macro-condition-edit.hpp" #include "macro-condition-edit.hpp"
#include "macro-export-import-dialog.hpp" #include "macro-export-import-dialog.hpp"
@@ -248,6 +249,7 @@ void AdvSceneSwitcher::ExportMacros()
} }
obs_data_set_array(data, "macros", macroArray); obs_data_set_array(data, "macros", macroArray);
SaveVariables(data); SaveVariables(data);
SaveActionQueues(data);
obs_data_set_string(data, "version", g_GIT_TAG); obs_data_set_string(data, "version", g_GIT_TAG);
auto json = obs_data_get_json(data); auto json = obs_data_get_json(data);
QString exportString(json); QString exportString(json);
@@ -370,26 +372,6 @@ bool AdvSceneSwitcher::ResolveMacroImportNameConflict(
return true; return true;
} }
static bool variableWithNameExists(const std::string &name)
{
return !!GetVariableByName(name);
}
static void importVariables(obs_data_t *obj)
{
OBSDataArrayAutoRelease array = obs_data_get_array(obj, "variables");
size_t count = obs_data_array_count(array);
for (size_t i = 0; i < count; i++) {
OBSDataAutoRelease data = obs_data_array_item(array, i);
auto var = Variable::Create();
var->Load(data);
if (variableWithNameExists(var->Name())) {
continue;
}
GetVariables().emplace_back(var);
}
}
void AdvSceneSwitcher::ImportMacros() void AdvSceneSwitcher::ImportMacros()
{ {
QString json; QString json;
@@ -404,7 +386,8 @@ void AdvSceneSwitcher::ImportMacros()
ImportMacros(); ImportMacros();
return; return;
} }
importVariables(data); ImportVariables(data);
ImportQueues(data);
auto version = obs_data_get_string(data, "version"); auto version = obs_data_get_string(data, "version");
if (strcmp(version, g_GIT_TAG) != 0) { if (strcmp(version, g_GIT_TAG) != 0) {
@@ -425,6 +408,7 @@ void AdvSceneSwitcher::ImportMacros()
OBSDataAutoRelease array_obj = obs_data_array_item(array, i); OBSDataAutoRelease array_obj = obs_data_array_item(array, i);
auto macro = std::make_shared<Macro>(); auto macro = std::make_shared<Macro>();
macro->Load(array_obj); macro->Load(array_obj);
RunPostLoadSteps();
if (macroNameExists(macro->Name()) && if (macroNameExists(macro->Name()) &&
!ResolveMacroImportNameConflict(macro)) { !ResolveMacroImportNameConflict(macro)) {

View File

@@ -358,6 +358,27 @@ void LoadActionQueues(obs_data_t *obj)
} }
} }
static bool queueWithNameExists(const std::string &name)
{
return !GetWeakActionQueueByName(name).expired();
}
void ImportQueues(obs_data_t *data)
{
OBSDataArrayAutoRelease array =
obs_data_get_array(data, "actionQueues");
size_t count = obs_data_array_count(array);
for (size_t i = 0; i < count; i++) {
OBSDataAutoRelease arrayElement = obs_data_array_item(array, i);
auto queue = ActionQueue::Create();
queue->Load(arrayElement);
if (queueWithNameExists(queue->Name())) {
continue;
}
queues.emplace_back(queue);
}
}
std::weak_ptr<ActionQueue> GetWeakActionQueueByName(const std::string &name) std::weak_ptr<ActionQueue> GetWeakActionQueueByName(const std::string &name)
{ {
for (const auto &queue : queues) { for (const auto &queue : queues) {

View File

@@ -98,6 +98,7 @@ signals:
void SetupActionQueues(); void SetupActionQueues();
void SaveActionQueues(obs_data_t *); void SaveActionQueues(obs_data_t *);
void LoadActionQueues(obs_data_t *); void LoadActionQueues(obs_data_t *);
void ImportQueues(obs_data_t *);
std::weak_ptr<ActionQueue> GetWeakActionQueueByName(const std::string &name); std::weak_ptr<ActionQueue> GetWeakActionQueueByName(const std::string &name);
std::weak_ptr<ActionQueue> GetWeakActionQueueByQString(const QString &name); std::weak_ptr<ActionQueue> GetWeakActionQueueByQString(const QString &name);
std::string GetActionQueueName(const std::weak_ptr<ActionQueue> &); std::string GetActionQueueName(const std::weak_ptr<ActionQueue> &);

View File

@@ -0,0 +1,63 @@
#include "double-slider.hpp"
#include <QWheelEvent>
#include <cmath>
namespace advss {
SliderIgnoreScroll::SliderIgnoreScroll(QWidget *parent) : QSlider(parent)
{
setFocusPolicy(Qt::StrongFocus);
}
SliderIgnoreScroll::SliderIgnoreScroll(Qt::Orientation orientation,
QWidget *parent)
: QSlider(parent)
{
setFocusPolicy(Qt::StrongFocus);
setOrientation(orientation);
}
void SliderIgnoreScroll::wheelEvent(QWheelEvent *event)
{
if (!hasFocus()) {
event->ignore();
} else {
QSlider::wheelEvent(event);
}
}
DoubleSlider::DoubleSlider(QWidget *parent) : SliderIgnoreScroll(parent)
{
connect(this, &DoubleSlider::valueChanged, [this](int val) {
emit DoubleValChanged((minVal / minStep + val) * minStep);
});
}
void DoubleSlider::SetDoubleConstraints(double newMin, double newMax,
double newStep, double val)
{
minVal = newMin;
maxVal = newMax;
minStep = newStep;
double total = maxVal - minVal;
int intMax = int(total / minStep);
setMinimum(0);
setMaximum(intMax);
setSingleStep(1);
SetDoubleVal(val);
}
double DoubleSlider::DoubleValue()
{
return (minVal / minStep + value()) * minStep;
}
void DoubleSlider::SetDoubleVal(double val)
{
setValue(lround((val - minVal) / minStep));
}
} // namespace advss

View File

@@ -0,0 +1,40 @@
// Based on OBS DoubleSlider and SliderIgnoreScroll implementation
#pragma once
#include "export-symbol-helper.hpp"
#include <QSlider>
namespace advss {
class SliderIgnoreScroll : public QSlider {
Q_OBJECT
public:
SliderIgnoreScroll(QWidget *parent = nullptr);
SliderIgnoreScroll(Qt::Orientation orientation,
QWidget *parent = nullptr);
protected:
virtual void wheelEvent(QWheelEvent *event) override;
};
class ADVSS_EXPORT DoubleSlider : public SliderIgnoreScroll {
Q_OBJECT
double minVal = 0, maxVal = 100, minStep = 1;
public:
DoubleSlider(QWidget *parent = nullptr);
void SetDoubleConstraints(double newMin, double newMax, double newStep,
double val);
double DoubleValue();
signals:
void DoubleValChanged(double val);
public slots:
void SetDoubleVal(double val);
};
} // namespace advss

View File

@@ -90,15 +90,17 @@ void VolControl::setPeakMeterType(enum obs_peak_meter_type peakMeterType)
} }
VolumeSlider::VolumeSlider(obs_fader_t *fader, QWidget *parent) VolumeSlider::VolumeSlider(obs_fader_t *fader, QWidget *parent)
: QSlider(parent) : DoubleSlider(parent)
{ {
fad = fader; fad = fader;
} }
VolumeSlider::VolumeSlider(obs_fader_t *fader, Qt::Orientation orientation, VolumeSlider::VolumeSlider(obs_fader_t *fader, Qt::Orientation orientation,
QWidget *parent) QWidget *parent)
: QSlider(orientation, parent) : DoubleSlider(parent)
{ {
SetDoubleConstraints(0, 100, 0.01, 0);
setOrientation(orientation);
fad = fader; fad = fader;
} }
@@ -219,7 +221,6 @@ VolControl::VolControl(OBSSource source_, bool showConfig, bool vertical)
nameLabel->setText(sourceName); nameLabel->setText(sourceName);
slider->setMinimum(0); slider->setMinimum(0);
slider->setMaximum(int(FADER_PRECISION));
obs_fader_add_callback(obs_fader, OBSVolumeChanged, this); obs_fader_add_callback(obs_fader, OBSVolumeChanged, this);
obs_volmeter_add_callback(obs_volmeter, OBSVolumeLevel, this); obs_volmeter_add_callback(obs_volmeter, OBSVolumeLevel, this);
@@ -592,7 +593,9 @@ void VolumeMeter::mousePressEvent(QMouseEvent *event)
VolumeMeter::VolumeMeter(QWidget *parent, obs_volmeter_t *obs_volmeter, VolumeMeter::VolumeMeter(QWidget *parent, obs_volmeter_t *obs_volmeter,
bool vertical) bool vertical)
: QWidget(parent), obs_volmeter(obs_volmeter), vertical(vertical) : QWidget(parent),
obs_volmeter(obs_volmeter),
vertical(vertical)
{ {
setAttribute(Qt::WA_OpaquePaintEvent, true); setAttribute(Qt::WA_OpaquePaintEvent, true);

View File

@@ -1,4 +1,5 @@
#pragma once #pragma once
#include "double-slider.hpp"
#include <obs.hpp> #include <obs.hpp>
#include <QWidget> #include <QWidget>
@@ -284,7 +285,7 @@ private:
QLabel *nameLabel; QLabel *nameLabel;
QLabel *volLabel; QLabel *volLabel;
VolumeMeter *volMeter; VolumeMeter *volMeter;
QSlider *slider; DoubleSlider *slider;
QPushButton *config = nullptr; QPushButton *config = nullptr;
float levelTotal; float levelTotal;
float levelCount; float levelCount;
@@ -327,10 +328,10 @@ public:
inline void SetContextMenu(QMenu *cm) { contextMenu = cm; } inline void SetContextMenu(QMenu *cm) { contextMenu = cm; }
void refreshColors(); void refreshColors();
QSlider *GetSlider() const { return slider; } DoubleSlider *GetSlider() const { return slider; }
}; };
class VolumeSlider : public QSlider { class VolumeSlider : public DoubleSlider {
Q_OBJECT Q_OBJECT
public: public:

View File

@@ -187,6 +187,28 @@ void LoadVariables(obs_data_t *obj)
obs_data_array_release(variablesArray); obs_data_array_release(variablesArray);
} }
static bool variableWithNameExists(const std::string &name)
{
return !!GetVariableByName(name);
}
void ImportVariables(obs_data_t *data)
{
obs_data_array_t *array = obs_data_get_array(data, "variables");
size_t count = obs_data_array_count(array);
for (size_t i = 0; i < count; i++) {
obs_data_t *arrayElement = obs_data_array_item(array, i);
auto var = Variable::Create();
var->Load(arrayElement);
obs_data_release(arrayElement);
if (variableWithNameExists(var->Name())) {
continue;
}
GetVariables().emplace_back(var);
}
obs_data_array_release(array);
}
static void populateSaveActionSelection(QComboBox *list) static void populateSaveActionSelection(QComboBox *list)
{ {
list->addItem( list->addItem(

View File

@@ -88,6 +88,7 @@ signals:
void SaveVariables(obs_data_t *obj); void SaveVariables(obs_data_t *obj);
void LoadVariables(obs_data_t *obj); void LoadVariables(obs_data_t *obj);
void ImportVariables(obs_data_t *obj);
std::deque<std::shared_ptr<Item>> &GetVariables(); std::deque<std::shared_ptr<Item>> &GetVariables();

View File

@@ -253,7 +253,7 @@ bool MacroConditionAudio::Save(obs_data_t *obj) const
static_cast<int>(_volumeCondition)); static_cast<int>(_volumeCondition));
obs_data_set_bool(obj, "useDb", _useDb); obs_data_set_bool(obj, "useDb", _useDb);
_volumeDB.Save(obj, "volumeDB"); _volumeDB.Save(obj, "volumeDB");
obs_data_set_int(obj, "version", 2); obs_data_set_int(obj, "version", 3);
return true; return true;
} }
@@ -294,9 +294,19 @@ bool MacroConditionAudio::Load(obs_data_t *obj)
_volumeCondition = static_cast<VolumeCondition>( _volumeCondition = static_cast<VolumeCondition>(
obs_data_get_int(obj, "volumeCondition")); obs_data_get_int(obj, "volumeCondition"));
_volmeter = AddVolmeterToSource(this, _audioSource.GetSource()); _volmeter = AddVolmeterToSource(this, _audioSource.GetSource());
if (obs_data_get_int(obj, "version") != 2) {
if (obs_data_get_int(obj, "version") < 2) {
// Set default values for dB handling
_useDb = false; _useDb = false;
_volumeDB = 0.0; _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 { } else {
_useDb = obs_data_get_bool(obj, "useDb"); _useDb = obs_data_get_bool(obj, "useDb");
_volumeDB.Load(obj, "volumeDB"); _volumeDB.Load(obj, "volumeDB");
@@ -416,7 +426,7 @@ MacroConditionAudioEdit::MacroConditionAudioEdit(
_checkTypes(new QComboBox()), _checkTypes(new QComboBox()),
_sources(new SourceSelectionWidget(this, QStringList(), true)), _sources(new SourceSelectionWidget(this, QStringList(), true)),
_condition(new QComboBox()), _condition(new QComboBox()),
_volumePercent(new VariableSpinBox()), _volumePercent(new VariableDoubleSpinBox()),
_volumeDB(new VariableDoubleSpinBox), _volumeDB(new VariableDoubleSpinBox),
_percentDBToggle(new QPushButton), _percentDBToggle(new QPushButton),
_syncOffset(new VariableSpinBox()), _syncOffset(new VariableSpinBox()),
@@ -444,8 +454,9 @@ MacroConditionAudioEdit::MacroConditionAudioEdit(
SLOT(CheckTypeChanged(int))); SLOT(CheckTypeChanged(int)));
QWidget::connect( QWidget::connect(
_volumePercent, _volumePercent,
SIGNAL(NumberVariableChanged(const NumberVariable<int> &)), SIGNAL(NumberVariableChanged(const NumberVariable<double> &)),
this, SLOT(VolumePercentChanged(const NumberVariable<int> &))); this,
SLOT(VolumePercentChanged(const NumberVariable<double> &)));
QWidget::connect( QWidget::connect(
_syncOffset, _syncOffset,
SIGNAL(NumberVariableChanged(const NumberVariable<int> &)), SIGNAL(NumberVariableChanged(const NumberVariable<int> &)),
@@ -506,11 +517,12 @@ void MacroConditionAudioEdit::UpdateVolmeterSource()
auto layout = this->layout(); auto layout = this->layout();
layout->addWidget(_volMeter); layout->addWidget(_volMeter);
QWidget::connect(_volMeter->GetSlider(), &QSlider::valueChanged, QWidget::connect(_volMeter->GetSlider(),
[=](int) { SyncSliderAndValueSelection(true); }); &DoubleSlider::DoubleValChanged,
[=](double) { SyncSliderAndValueSelection(true); });
// Slider will default to 0 so set it manually once // Slider will default to 0 so set it manually once
_volMeter->GetSlider()->setValue(_entryData->_volumePercent); SyncSliderAndValueSelection(false);
} }
void MacroConditionAudioEdit::SourceChanged(const SourceSelection &source) void MacroConditionAudioEdit::SourceChanged(const SourceSelection &source)
@@ -531,7 +543,7 @@ void MacroConditionAudioEdit::SourceChanged(const SourceSelection &source)
} }
void MacroConditionAudioEdit::VolumePercentChanged( void MacroConditionAudioEdit::VolumePercentChanged(
const NumberVariable<int> &vol) const NumberVariable<double> &vol)
{ {
if (_loading || !_entryData) { if (_loading || !_entryData) {
return; return;
@@ -600,35 +612,35 @@ void MacroConditionAudioEdit::PercentDBClicked()
void MacroConditionAudioEdit::SyncSliderAndValueSelection(bool sliderMoved) void MacroConditionAudioEdit::SyncSliderAndValueSelection(bool sliderMoved)
{ {
if (_loading || !_entryData) { if (!_entryData) {
return; return;
} }
if (sliderMoved) { if (sliderMoved) {
auto sliderPosition = _volMeter->GetSlider()->DoubleValue();
// Adjust to the dB scale on the volume meter widget
auto dBScaleValue = ((sliderPosition * 3.0) / 5.0) - 60.0;
if (_entryData->_useDb) { if (_entryData->_useDb) {
_volumeDB->SetFixedValue(PercentToDecibel( _volumeDB->SetFixedValue(dBScaleValue);
(float)_volMeter->GetSlider()->value() /
100.0));
const QSignalBlocker blocker(this); const QSignalBlocker blocker(this);
_volumePercent->SetFixedValue( _volumePercent->SetFixedValue(
_volMeter->GetSlider()->value()); DecibelToPercent(dBScaleValue) * 100);
} else { } else {
_volumePercent->SetFixedValue( _volumePercent->SetFixedValue(
_volMeter->GetSlider()->value()); DecibelToPercent(dBScaleValue) * 100);
const QSignalBlocker blocker(this); const QSignalBlocker blocker(this);
_volumeDB->SetFixedValue(PercentToDecibel( _volumeDB->SetFixedValue(dBScaleValue);
(float)_volMeter->GetSlider()->value() /
100.0));
} }
} else { } else {
const QSignalBlocker blocker(this); const QSignalBlocker blocker(this);
if (_entryData->_useDb) { double dBValue =
_volMeter->GetSlider()->setValue( _entryData->_useDb
DecibelToPercent(_entryData->_volumeDB) * 100); ? _entryData->_volumeDB.GetFixedValue()
} else { : PercentToDecibel(_entryData->_volumePercent /
_volMeter->GetSlider()->setValue( 100.0);
_entryData->_volumePercent); auto sliderPosition = (dBValue + 60.0) * 5 / 3.0;
} _volMeter->GetSlider()->SetDoubleVal(sliderPosition);
} }
} }

View File

@@ -55,11 +55,11 @@ public:
SourceSelection _audioSource; SourceSelection _audioSource;
bool _useDb = false; bool _useDb = false;
NumberVariable<int> _volumePercent = 0; DoubleVariable _volumePercent = 0;
NumberVariable<double> _volumeDB = 0.0; DoubleVariable _volumeDB = 0.0;
NumberVariable<int> _syncOffset = 0; IntVariable _syncOffset = 0;
obs_monitoring_type _monitorType = OBS_MONITORING_TYPE_NONE; obs_monitoring_type _monitorType = OBS_MONITORING_TYPE_NONE;
NumberVariable<double> _balance = 0.5; DoubleVariable _balance = 0.5;
OutputCondition _outputCondition = OutputCondition::ABOVE; OutputCondition _outputCondition = OutputCondition::ABOVE;
VolumeCondition _volumeCondition = VolumeCondition::ABOVE; VolumeCondition _volumeCondition = VolumeCondition::ABOVE;
obs_volmeter_t *_volmeter = nullptr; obs_volmeter_t *_volmeter = nullptr;
@@ -97,7 +97,7 @@ public:
private slots: private slots:
void SourceChanged(const SourceSelection &); void SourceChanged(const SourceSelection &);
void VolumePercentChanged(const NumberVariable<int> &vol); void VolumePercentChanged(const NumberVariable<double> &vol);
void ConditionChanged(int cond); void ConditionChanged(int cond);
void CheckTypeChanged(int cond); void CheckTypeChanged(int cond);
void SyncOffsetChanged(const NumberVariable<int> &value); void SyncOffsetChanged(const NumberVariable<int> &value);
@@ -117,7 +117,7 @@ private:
QComboBox *_checkTypes; QComboBox *_checkTypes;
SourceSelectionWidget *_sources; SourceSelectionWidget *_sources;
QComboBox *_condition; QComboBox *_condition;
VariableSpinBox *_volumePercent; VariableDoubleSpinBox *_volumePercent;
VariableDoubleSpinBox *_volumeDB; VariableDoubleSpinBox *_volumeDB;
QPushButton *_percentDBToggle; QPushButton *_percentDBToggle;
VariableSpinBox *_syncOffset; VariableSpinBox *_syncOffset;

View File

@@ -17,6 +17,55 @@ namespace advss {
std::map<std::pair<MidiDeviceType, std::string>, MidiDeviceInstance *> std::map<std::pair<MidiDeviceType, std::string>, MidiDeviceInstance *>
MidiDeviceInstance::devices = {}; MidiDeviceInstance::devices = {};
static bool setupDeviceObservers()
{
static std::vector<libremidi::observer> observers;
for (auto api : libremidi::available_apis()) {
libremidi::observer_configuration cbs;
cbs.input_added = [=](const libremidi::input_port &p) {
auto dev = MidiDeviceInstance::GetDevice(p);
if (!dev) {
return;
}
blog(LOG_INFO, "MIDI input connected: %s",
p.port_name.c_str());
dev->ClosePort();
dev->OpenPort();
};
cbs.input_removed = [=](const libremidi::input_port &p) {
auto dev = MidiDeviceInstance::GetDevice(p);
if (!dev) {
return;
}
blog(LOG_INFO, "MIDI input removed: %s",
p.port_name.c_str());
};
cbs.output_added = [=](const libremidi::output_port &p) {
auto dev = MidiDeviceInstance::GetDevice(p);
if (!dev) {
return;
}
blog(LOG_INFO, "MIDI output connected: %s",
p.port_name.c_str());
dev->ClosePort();
dev->OpenPort();
};
cbs.output_removed = [=](const libremidi::output_port &p) {
auto dev = MidiDeviceInstance::GetDevice(p);
if (!dev) {
return;
}
blog(LOG_INFO, "MIDI output removed: %s",
p.port_name.c_str());
};
observers.emplace_back(
cbs, libremidi::observer_configuration_for(api));
}
return true;
}
static bool deviceObserversAreSetup = setupDeviceObservers();
void MidiDeviceInstance::ResetAllDevices() void MidiDeviceInstance::ResetAllDevices()
{ {
for (auto const &[_, device] : MidiDeviceInstance::devices) { for (auto const &[_, device] : MidiDeviceInstance::devices) {
@@ -212,8 +261,9 @@ bool MidiMessage::Matches(const MidiMessage &m) const
return channelMatch && noteMatch && valueMatch && typeMatch; return channelMatch && noteMatch && valueMatch && typeMatch;
} }
MidiDeviceInstance *MidiDeviceInstance::GetDevice(MidiDeviceType type, MidiDeviceInstance *
const std::string &name) MidiDeviceInstance::GetDeviceAndOpen(MidiDeviceType type,
const std::string &name)
{ {
if (name.empty()) { if (name.empty()) {
return nullptr; return nullptr;
@@ -252,6 +302,30 @@ getNameFromPortInformation(const libremidi::port_information &info)
return "[" + name; return "[" + name;
} }
MidiDeviceInstance *
advss::MidiDeviceInstance::GetDevice(const libremidi::input_port &p)
{
auto key = std::make_pair(MidiDeviceType::INPUT,
getNameFromPortInformation(p));
auto it = MidiDeviceInstance::devices.find(key);
if (it == devices.end()) {
return nullptr;
}
return it->second;
}
MidiDeviceInstance *
advss::MidiDeviceInstance::GetDevice(const libremidi::output_port &p)
{
auto key = std::make_pair(MidiDeviceType::OUTPUT,
getNameFromPortInformation(p));
auto it = MidiDeviceInstance::devices.find(key);
if (it == devices.end()) {
return nullptr;
}
return it->second;
}
static inline QStringList getInputDeviceNames() static inline QStringList getInputDeviceNames()
{ {
QStringList devices; QStringList devices;
@@ -305,10 +379,11 @@ static std::string getPortNameFromNumber(MidiDeviceType type, int port)
return devices.at(port).toStdString(); return devices.at(port).toStdString();
} }
MidiDeviceInstance *MidiDeviceInstance::GetDevice(MidiDeviceType type, int port) MidiDeviceInstance *MidiDeviceInstance::GetDeviceAndOpen(MidiDeviceType type,
int port)
{ {
std::string name = getPortNameFromNumber(type, port); std::string name = getPortNameFromNumber(type, port);
return GetDevice(type, name); return GetDeviceAndOpen(type, name);
} }
void MidiDevice::Save(obs_data_t *obj) const void MidiDevice::Save(obs_data_t *obj) const
@@ -327,13 +402,13 @@ void MidiDevice::Load(obs_data_t *obj)
// TODO: Remove this fallback at some point // TODO: Remove this fallback at some point
if (obs_data_has_user_value(data, "port")) { if (obs_data_has_user_value(data, "port")) {
auto port = obs_data_get_int(data, "port"); auto port = obs_data_get_int(data, "port");
_dev = MidiDeviceInstance::GetDevice(_type, port); _dev = MidiDeviceInstance::GetDeviceAndOpen(_type, port);
if (_dev) { if (_dev) {
_name = _dev->_name; _name = _dev->_name;
} }
} else { } else {
_name = obs_data_get_string(data, "portName"); _name = obs_data_get_string(data, "portName");
_dev = MidiDeviceInstance::GetDevice(_type, _name); _dev = MidiDeviceInstance::GetDeviceAndOpen(_type, _name);
} }
} }
@@ -372,8 +447,7 @@ getInPortFromName(const std::string &name)
bool MidiDeviceInstance::OpenPort() bool MidiDeviceInstance::OpenPort()
{ {
if ((_type == MidiDeviceType::INPUT && _in.is_port_open()) || if (IsOpened()) {
(_type == MidiDeviceType::OUTPUT && _out.is_port_open())) {
return true; return true;
} }
@@ -437,10 +511,15 @@ bool MidiDeviceInstance::OpenPort()
return false; return false;
} }
bool MidiDeviceInstance::IsOpened() const
{
return (_type == MidiDeviceType::INPUT && _in.is_port_open()) ||
(_type == MidiDeviceType::OUTPUT && _out.is_port_open());
}
void MidiDeviceInstance::ClosePort() void MidiDeviceInstance::ClosePort()
{ {
if ((_type == MidiDeviceType::INPUT && !_in.is_port_open()) || if (!IsOpened()) {
(_type == MidiDeviceType::OUTPUT && !_out.is_port_open())) {
return; return;
} }
@@ -542,7 +621,6 @@ MidiMessageBuffer MidiDeviceInstance::RegisterForMidiMessages()
void MidiDeviceInstance::ReceiveMidiMessage(libremidi::message &&msg) void MidiDeviceInstance::ReceiveMidiMessage(libremidi::message &&msg)
{ {
auto lock = LockContext();
_dispatcher.DispatchMessage(msg); _dispatcher.DispatchMessage(msg);
vblog(LOG_INFO, "received midi: %s", vblog(LOG_INFO, "received midi: %s",
MidiMessage::ToString(msg).c_str()); MidiMessage::ToString(msg).c_str());
@@ -590,7 +668,7 @@ void MidiDeviceSelection::IdxChangedHelper(int idx)
} }
auto name = currentText().toStdString(); auto name = currentText().toStdString();
auto devInstance = MidiDeviceInstance::GetDevice(_type, name); auto devInstance = MidiDeviceInstance::GetDeviceAndOpen(_type, name);
if (!devInstance) { if (!devInstance) {
DisplayMessage(obs_module_text( DisplayMessage(obs_module_text(
"AdvSceneSwitcher.midi.deviceOpenFail")); "AdvSceneSwitcher.midi.deviceOpenFail"));

View File

@@ -64,16 +64,22 @@ enum class MidiDeviceType {
class MidiDeviceInstance { class MidiDeviceInstance {
public: public:
static MidiDeviceInstance *GetDevice(MidiDeviceType type, static MidiDeviceInstance *GetDeviceAndOpen(MidiDeviceType type,
const std::string &); const std::string &);
static MidiDeviceInstance *GetDevice(MidiDeviceType type, int port); static MidiDeviceInstance *GetDeviceAndOpen(MidiDeviceType type,
int port);
static MidiDeviceInstance *GetDevice(const libremidi::input_port &p);
static MidiDeviceInstance *GetDevice(const libremidi::output_port &p);
static void ResetAllDevices(); static void ResetAllDevices();
bool OpenPort();
void ClosePort();
private: private:
MidiDeviceInstance() = default; MidiDeviceInstance() = default;
~MidiDeviceInstance() = default; ~MidiDeviceInstance() = default;
bool OpenPort(); bool IsOpened() const;
void ClosePort();
bool SendMessge(const MidiMessage &); bool SendMessge(const MidiMessage &);
[[nodiscard]] MidiMessageBuffer RegisterForMidiMessages(); [[nodiscard]] MidiMessageBuffer RegisterForMidiMessages();
void ReceiveMidiMessage(libremidi::message &&); void ReceiveMidiMessage(libremidi::message &&);