diff --git a/data/locale/en-US.ini b/data/locale/en-US.ini index 011d2b57..80a5d4d0 100644 --- a/data/locale/en-US.ini +++ b/data/locale/en-US.ini @@ -113,7 +113,8 @@ AdvSceneSwitcher.condition.audio.state.mute="muted" AdvSceneSwitcher.condition.audio.state.unmute="unmuted" AdvSceneSwitcher.condition.audio.type.output="Output volume" AdvSceneSwitcher.condition.audio.type.volume="Configured volume level" -AdvSceneSwitcher.condition.audio.entry="{{checkType}} of {{audioSources}} is {{condition}} {{volume}}" +AdvSceneSwitcher.condition.audio.type.syncOffset="Sync offset" +AdvSceneSwitcher.condition.audio.entry="{{checkType}} of {{audioSources}} is {{condition}}{{volume}}{{syncOffset}}" AdvSceneSwitcher.condition.cursor="Cursor" AdvSceneSwitcher.condition.cursor.type.region="is in region" AdvSceneSwitcher.condition.cursor.type.moving="is moving" @@ -380,13 +381,14 @@ AdvSceneSwitcher.action.audio.type.mute="Mute" AdvSceneSwitcher.action.audio.type.unmute="Unmute" AdvSceneSwitcher.action.audio.type.sourceVolume="Set source volume" AdvSceneSwitcher.action.audio.type.masterVolume="Set master volume" +AdvSceneSwitcher.action.audio.type.syncOffset="Set sync offset" AdvSceneSwitcher.action.audio.fade.type.duration="over a duration of" AdvSceneSwitcher.action.audio.fade.type.rate="at a rate of" AdvSceneSwitcher.action.audio.fade.duration="{{fade}}Fade {{fadeTypes}} {{duration}} seconds." AdvSceneSwitcher.action.audio.fade.rate="{{fade}}Fade {{fadeTypes}} {{rate}}per second." AdvSceneSwitcher.action.audio.fade.wait="Wait for fade to complete." AdvSceneSwitcher.action.audio.fade.abort="Abort already active fade." -AdvSceneSwitcher.action.audio.entry="{{actions}} {{audioSources}} {{volume}}" +AdvSceneSwitcher.action.audio.entry="{{actions}}{{audioSources}}{{volume}}{{syncOffset}}" AdvSceneSwitcher.action.recording="Recording" AdvSceneSwitcher.action.recording.type.stop="Stop recording" AdvSceneSwitcher.action.recording.type.start="Start recording" diff --git a/data/locale/es-ES.ini b/data/locale/es-ES.ini index e89a0444..05474012 100644 --- a/data/locale/es-ES.ini +++ b/data/locale/es-ES.ini @@ -106,7 +106,7 @@ AdvSceneSwitcher.condition.audio.state.mute="silenciado" AdvSceneSwitcher.condition.audio.state.unmute="no silenciado" AdvSceneSwitcher.condition.audio.type.output="Volumen de salida" AdvSceneSwitcher.condition.audio.type.volume="Nivel de volumen configurado" -AdvSceneSwitcher.condition.audio.entry="{{checkType}} de {{audioSources}} es {{condition}} {{volume}}" +AdvSceneSwitcher.condition.audio.entry="{{checkType}} de {{audioSources}} es {{condition}}{{volume}}{{syncOffset}}" AdvSceneSwitcher.condition.cursor="Cursor" AdvSceneSwitcher.condition.cursor.type.region="está en la región" AdvSceneSwitcher.condition.cursor.type.moving="se está moviendo" @@ -348,7 +348,7 @@ AdvSceneSwitcher.action.audio.fade.duration="{{fade}}Fade {{fadeTypes}} {{durati AdvSceneSwitcher.action.audio.fade.rate="{{fade}}Fade {{fadeTypes}} {{rate}}por segundo." AdvSceneSwitcher.action.audio.fade.wait="Espere a que se complete el desvanecimiento". AdvSceneSwitcher.action.audio.fade.abort="Cancelar atenuación ya activa." -AdvSceneSwitcher.action.audio.entry="{{actions}} {{audioSources}} {{volume}}" +AdvSceneSwitcher.action.audio.entry="{{actions}}{{audioSources}}{{volume}}{{syncOffset}}" AdvSceneSwitcher.action.recording="Grabando" AdvSceneSwitcher.action.recording.type.stop="Detener grabación" AdvSceneSwitcher.action.recording.type.start="Iniciar grabación" diff --git a/data/locale/ru-RU.ini b/data/locale/ru-RU.ini index 087a1d3d..6d6dab71 100644 --- a/data/locale/ru-RU.ini +++ b/data/locale/ru-RU.ini @@ -129,7 +129,7 @@ AdvSceneSwitcher.action.audio.type.mute="Замьютить" AdvSceneSwitcher.action.audio.type.unmute="Размьютить" AdvSceneSwitcher.action.audio.type.sourceVolume="Установить громкость источника" AdvSceneSwitcher.action.audio.type.masterVolume="Установить главную громкость" -AdvSceneSwitcher.action.audio.entry="{{actions}} {{audioSources}} {{volume}}" +AdvSceneSwitcher.action.audio.entry="{{actions}}{{audioSources}}{{volume}}{{syncOffset}}" AdvSceneSwitcher.action.recording="Запись" AdvSceneSwitcher.action.recording.type.stop="Остановить запись" AdvSceneSwitcher.action.recording.type.start="Начать запись" diff --git a/data/locale/tr-TR.ini b/data/locale/tr-TR.ini index 8f782867..2501a8c3 100644 --- a/data/locale/tr-TR.ini +++ b/data/locale/tr-TR.ini @@ -277,7 +277,7 @@ AdvSceneSwitcher.action.audio.type.mute="Sessiz" AdvSceneSwitcher.action.audio.type.unmute="Ses açmak" AdvSceneSwitcher.action.audio.type.sourceVolume="Kaynak ses seviyesini ayarla" AdvSceneSwitcher.action.audio.type.masterVolume="Ana ses seviyesini ayarla" -AdvSceneSwitcher.action.audio.entry="{{actions}} {{audioSources}} {{volume}}" +AdvSceneSwitcher.action.audio.entry="{{actions}}{{audioSources}}{{volume}}{{syncOffset}}" AdvSceneSwitcher.action.recording="Kayıt" AdvSceneSwitcher.action.recording.type.stop="Kayıt Durdur" AdvSceneSwitcher.action.recording.type.start="Kayıt Başlat" diff --git a/data/locale/zh-CN.ini b/data/locale/zh-CN.ini index 885b1fe4..6efe5982 100644 --- a/data/locale/zh-CN.ini +++ b/data/locale/zh-CN.ini @@ -318,7 +318,7 @@ AdvSceneSwitcher.action.audio.type.mute="静音" AdvSceneSwitcher.action.audio.type.unmute="取消静音" AdvSceneSwitcher.action.audio.type.sourceVolume="来源音量" AdvSceneSwitcher.action.audio.type.masterVolume="主音量" -AdvSceneSwitcher.action.audio.entry="{{actions}} {{audioSources}} {{volume}}" +AdvSceneSwitcher.action.audio.entry="{{actions}}{{audioSources}}{{volume}}{{syncOffset}}" AdvSceneSwitcher.action.recording="录制" AdvSceneSwitcher.action.recording.type.stop="停止录制" AdvSceneSwitcher.action.recording.type.start="开始录制" diff --git a/src/macro-core/macro-action-audio.cpp b/src/macro-core/macro-action-audio.cpp index 5ac9453d..0e518ab0 100644 --- a/src/macro-core/macro-action-audio.cpp +++ b/src/macro-core/macro-action-audio.cpp @@ -2,6 +2,7 @@ #include "advanced-scene-switcher.hpp" #include "utility.hpp" +constexpr int64_t nsPerMs = 1000000; const std::string MacroActionAudio::id = "audio"; bool MacroActionAudio::_registered = MacroActionFactory::Register( @@ -9,19 +10,24 @@ bool MacroActionAudio::_registered = MacroActionFactory::Register( {MacroActionAudio::Create, MacroActionAudioEdit::Create, "AdvSceneSwitcher.action.audio"}); -const static std::map actionTypes = { - {AudioAction::MUTE, "AdvSceneSwitcher.action.audio.type.mute"}, - {AudioAction::UNMUTE, "AdvSceneSwitcher.action.audio.type.unmute"}, - {AudioAction::SOURCE_VOLUME, +const static std::map actionTypes = { + {MacroActionAudio::Action::MUTE, + "AdvSceneSwitcher.action.audio.type.mute"}, + {MacroActionAudio::Action::UNMUTE, + "AdvSceneSwitcher.action.audio.type.unmute"}, + {MacroActionAudio::Action::SOURCE_VOLUME, "AdvSceneSwitcher.action.audio.type.sourceVolume"}, - {AudioAction::MASTER_VOLUME, + {MacroActionAudio::Action::MASTER_VOLUME, "AdvSceneSwitcher.action.audio.type.masterVolume"}, + {MacroActionAudio::Action::SYNC_OFFSET, + "AdvSceneSwitcher.action.audio.type.syncOffset"}, }; -const static std::map fadeTypes = { - {FadeType::DURATION, +const static std::map fadeTypes = { + {MacroActionAudio::FadeType::DURATION, "AdvSceneSwitcher.action.audio.fade.type.duration"}, - {FadeType::RATE, "AdvSceneSwitcher.action.audio.fade.type.rate"}, + {MacroActionAudio::FadeType::RATE, + "AdvSceneSwitcher.action.audio.fade.type.rate"}, }; constexpr auto fadeInterval = std::chrono::milliseconds(100); @@ -29,7 +35,7 @@ constexpr float minFade = 0.000001f; void MacroActionAudio::SetFadeActive(bool value) { - if (_action == AudioAction::SOURCE_VOLUME) { + if (_action == MacroActionAudio::Action::SOURCE_VOLUME) { switcher->activeAudioFades[GetWeakSourceName(_audioSource)] .active = value; } else { @@ -40,7 +46,7 @@ void MacroActionAudio::SetFadeActive(bool value) bool MacroActionAudio::FadeActive() { bool active = true; - if (_action == AudioAction::SOURCE_VOLUME) { + if (_action == MacroActionAudio::Action::SOURCE_VOLUME) { auto it = switcher->activeAudioFades.find( GetWeakSourceName(_audioSource)); if (it == switcher->activeAudioFades.end()) { @@ -57,7 +63,7 @@ bool MacroActionAudio::FadeActive() std::atomic_int *MacroActionAudio::GetFadeIdPtr() { - if (_action == AudioAction::SOURCE_VOLUME) { + if (_action == MacroActionAudio::Action::SOURCE_VOLUME) { auto it = switcher->activeAudioFades.find( GetWeakSourceName(_audioSource)); if (it == switcher->activeAudioFades.end()) { @@ -70,7 +76,7 @@ std::atomic_int *MacroActionAudio::GetFadeIdPtr() void MacroActionAudio::SetVolume(float vol) { - if (_action == AudioAction::SOURCE_VOLUME) { + if (_action == MacroActionAudio::Action::SOURCE_VOLUME) { auto s = obs_weak_source_get_source(_audioSource); obs_source_set_volume(s, vol); obs_source_release(s); @@ -82,7 +88,7 @@ void MacroActionAudio::SetVolume(float vol) float MacroActionAudio::GetVolume() { float curVol; - if (_action == AudioAction::SOURCE_VOLUME) { + if (_action == MacroActionAudio::Action::SOURCE_VOLUME) { auto s = obs_weak_source_get_source(_audioSource); if (!s) { return 0.; @@ -103,7 +109,7 @@ void MacroActionAudio::FadeVolume() float volDiff = (volIncrease) ? vol - curVol : curVol - vol; int nrSteps = 0; float volStep = 0.; - if (_fadeType == FadeType::DURATION) { + if (_fadeType == MacroActionAudio::FadeType::DURATION) { nrSteps = _duration.seconds * 1000 / fadeInterval.count(); volStep = volDiff / nrSteps; } else { @@ -139,14 +145,15 @@ void MacroActionAudio::FadeVolume() void MacroActionAudio::StartFade() { - if (_action == AudioAction::SOURCE_VOLUME && !_audioSource) { + if (_action == MacroActionAudio::Action::SOURCE_VOLUME && + !_audioSource) { return; } if (FadeActive() && !_abortActiveFade) { blog(LOG_WARNING, "Audio fade for volume of %s already active! New fade request will be ignored!", - (_action == AudioAction::SOURCE_VOLUME) + (_action == MacroActionAudio::Action::SOURCE_VOLUME) ? GetWeakSourceName(_audioSource).c_str() : "master volume"); return; @@ -165,20 +172,23 @@ bool MacroActionAudio::PerformAction() { auto s = obs_weak_source_get_source(_audioSource); switch (_action) { - case AudioAction::MUTE: + case MacroActionAudio::Action::MUTE: obs_source_set_muted(s, true); break; - case AudioAction::UNMUTE: + case MacroActionAudio::Action::UNMUTE: obs_source_set_muted(s, false); break; - case AudioAction::SOURCE_VOLUME: - case AudioAction::MASTER_VOLUME: + case MacroActionAudio::Action::SOURCE_VOLUME: + case MacroActionAudio::Action::MASTER_VOLUME: if (_fade) { StartFade(); } else { SetVolume((float)_volume / 100.0f); } break; + case MacroActionAudio::Action::SYNC_OFFSET: + obs_source_set_sync_offset(s, _syncOffset * nsPerMs); + break; default: break; } @@ -208,6 +218,7 @@ bool MacroActionAudio::Save(obs_data_t *obj) obs_data_set_string(obj, "audioSource", GetWeakSourceName(_audioSource).c_str()); obs_data_set_int(obj, "action", static_cast(_action)); + obs_data_set_int(obj, "syncOffset", _syncOffset); obs_data_set_int(obj, "volume", _volume); obs_data_set_double(obj, "rate", _rate); obs_data_set_bool(obj, "fade", _fade); @@ -223,7 +234,9 @@ bool MacroActionAudio::Load(obs_data_t *obj) _duration.Load(obj); const char *audioSourceName = obs_data_get_string(obj, "audioSource"); _audioSource = GetWeakSourceByName(audioSourceName); - _action = static_cast(obs_data_get_int(obj, "action")); + _action = static_cast( + obs_data_get_int(obj, "action")); + _syncOffset = obs_data_get_int(obj, "syncOffset"); _volume = obs_data_get_int(obj, "volume"); _rate = obs_data_get_double(obj, "rate"); _fade = obs_data_get_bool(obj, "fade"); @@ -233,10 +246,10 @@ bool MacroActionAudio::Load(obs_data_t *obj) _wait = false; } if (obs_data_has_user_value(obj, "fadeType")) { - _fadeType = static_cast( + _fadeType = static_cast( obs_data_get_int(obj, "fadeType")); } else { - _fadeType = FadeType::DURATION; + _fadeType = MacroActionAudio::FadeType::DURATION; } if (obs_data_has_user_value(obj, "abortActiveFade")) { _abortActiveFade = obs_data_get_bool(obj, "abortActiveFade"); @@ -274,6 +287,7 @@ MacroActionAudioEdit::MacroActionAudioEdit( _audioSources(new QComboBox), _actions(new QComboBox), _fadeTypes(new QComboBox), + _syncOffset(new QSpinBox), _volumePercent(new QSpinBox), _fade(new QCheckBox), _duration(new DurationSelection(parent, false)), @@ -285,6 +299,10 @@ MacroActionAudioEdit::MacroActionAudioEdit( _fadeTypeLayout(new QHBoxLayout), _fadeOptionsLayout(new QVBoxLayout) { + _syncOffset->setMinimum(-950); + _syncOffset->setMaximum(20000); + _syncOffset->setSuffix("ms"); + _volumePercent->setMinimum(0); _volumePercent->setMaximum(2000); _volumePercent->setSuffix("%"); @@ -302,6 +320,8 @@ MacroActionAudioEdit::MacroActionAudioEdit( QWidget::connect(_audioSources, SIGNAL(currentTextChanged(const QString &)), this, SLOT(SourceChanged(const QString &))); + QWidget::connect(_syncOffset, SIGNAL(valueChanged(int)), this, + SLOT(SyncOffsetChanged(int))); QWidget::connect(_volumePercent, SIGNAL(valueChanged(int)), this, SLOT(VolumeChanged(int))); QWidget::connect(_fade, SIGNAL(stateChanged(int)), this, @@ -320,6 +340,7 @@ MacroActionAudioEdit::MacroActionAudioEdit( std::unordered_map widgetPlaceholders = { {"{{audioSources}}", _audioSources}, {"{{actions}}", _actions}, + {"{{syncOffset}}", _syncOffset}, {"{{volume}}", _volumePercent}, {"{{fade}}", _fade}, {"{{duration}}", _duration}, @@ -350,21 +371,19 @@ MacroActionAudioEdit::MacroActionAudioEdit( _loading = false; } -bool hasVolumeControl(AudioAction action) +bool hasVolumeControl(MacroActionAudio::Action action) { - return action == AudioAction::SOURCE_VOLUME || - action == AudioAction::MASTER_VOLUME; -} - -bool hasSourceControl(AudioAction action) -{ - return action != AudioAction::MASTER_VOLUME; + return action == MacroActionAudio::Action::SOURCE_VOLUME || + action == MacroActionAudio::Action::MASTER_VOLUME; } void MacroActionAudioEdit::SetWidgetVisibility() { _volumePercent->setVisible(hasVolumeControl(_entryData->_action)); - _audioSources->setVisible(hasSourceControl(_entryData->_action)); + _audioSources->setVisible(_entryData->_action != + MacroActionAudio::Action::MASTER_VOLUME); + _syncOffset->setVisible(_entryData->_action == + MacroActionAudio::Action::SYNC_OFFSET); _fadeTypes->setDisabled(!_entryData->_fade); _wait->setDisabled(!_entryData->_fade); @@ -383,7 +402,7 @@ void MacroActionAudioEdit::SetWidgetVisibility() {"{{rate}}", _rate}, {"{{fadeTypes}}", _fadeTypes}, }; - if (_entryData->_fadeType == FadeType::DURATION) { + if (_entryData->_fadeType == MacroActionAudio::FadeType::DURATION) { placeWidgets( obs_module_text( "AdvSceneSwitcher.action.audio.fade.duration"), @@ -394,8 +413,10 @@ void MacroActionAudioEdit::SetWidgetVisibility() _fadeTypeLayout, widgetPlaceholders); } - _duration->setVisible(_entryData->_fadeType == FadeType::DURATION); - _rate->setVisible(_entryData->_fadeType == FadeType::RATE); + _duration->setVisible(_entryData->_fadeType == + MacroActionAudio::FadeType::DURATION); + _rate->setVisible(_entryData->_fadeType == + MacroActionAudio::FadeType::RATE); setLayoutVisible(_fadeTypeLayout, hasVolumeControl(_entryData->_action)); @@ -413,6 +434,7 @@ void MacroActionAudioEdit::UpdateEntryData() _audioSources->setCurrentText( GetWeakSourceName(_entryData->_audioSource).c_str()); _actions->setCurrentIndex(static_cast(_entryData->_action)); + _syncOffset->setValue(_entryData->_syncOffset); _volumePercent->setValue(_entryData->_volume); _fade->setChecked(_entryData->_fade); _duration->SetDuration(_entryData->_duration); @@ -442,10 +464,20 @@ void MacroActionAudioEdit::ActionChanged(int value) } std::lock_guard lock(switcher->m); - _entryData->_action = static_cast(value); + _entryData->_action = static_cast(value); SetWidgetVisibility(); } +void MacroActionAudioEdit::SyncOffsetChanged(int value) +{ + if (_loading || !_entryData) { + return; + } + + std::lock_guard lock(switcher->m); + _entryData->_syncOffset = value; +} + void MacroActionAudioEdit::VolumeChanged(int value) { if (_loading || !_entryData) { @@ -514,6 +546,6 @@ void MacroActionAudioEdit::FadeTypeChanged(int value) } std::lock_guard lock(switcher->m); - _entryData->_fadeType = static_cast(value); + _entryData->_fadeType = static_cast(value); SetWidgetVisibility(); } diff --git a/src/macro-core/macro-action-audio.hpp b/src/macro-core/macro-action-audio.hpp index fe59a713..ee0b3d5a 100644 --- a/src/macro-core/macro-action-audio.hpp +++ b/src/macro-core/macro-action-audio.hpp @@ -7,18 +7,6 @@ #include #include -enum class AudioAction { - MUTE, - UNMUTE, - SOURCE_VOLUME, - MASTER_VOLUME, -}; - -enum class FadeType { - DURATION, - RATE, -}; - class MacroActionAudio : public MacroAction { public: MacroActionAudio(Macro *m) : MacroAction(m) {} @@ -34,8 +22,23 @@ public: } OBSWeakSource _audioSource; - AudioAction _action = AudioAction::MUTE; + + enum class Action { + MUTE, + UNMUTE, + SOURCE_VOLUME, + MASTER_VOLUME, + SYNC_OFFSET, + }; + + enum class FadeType { + DURATION, + RATE, + }; + + Action _action = Action::MUTE; FadeType _fadeType = FadeType::DURATION; + int64_t _syncOffset = 0; int _volume = 0; bool _fade = false; Duration _duration; @@ -75,6 +78,7 @@ public: private slots: void SourceChanged(const QString &text); void ActionChanged(int value); + void SyncOffsetChanged(int value); void VolumeChanged(int value); void FadeChanged(int value); void DurationChanged(double seconds); @@ -89,6 +93,7 @@ protected: QComboBox *_audioSources; QComboBox *_actions; QComboBox *_fadeTypes; + QSpinBox *_syncOffset; QSpinBox *_volumePercent; QCheckBox *_fade; DurationSelection *_duration; diff --git a/src/macro-core/macro-condition-audio.cpp b/src/macro-core/macro-condition-audio.cpp index 17c24973..db41e567 100644 --- a/src/macro-core/macro-condition-audio.cpp +++ b/src/macro-core/macro-condition-audio.cpp @@ -3,6 +3,7 @@ #include "utility.hpp" #include "advanced-scene-switcher.hpp" +constexpr int64_t nsPerMs = 1000000; const std::string MacroConditionAudio::id = "audio"; bool MacroConditionAudio::_registered = MacroConditionFactory::Register( @@ -10,24 +11,35 @@ bool MacroConditionAudio::_registered = MacroConditionFactory::Register( {MacroConditionAudio::Create, MacroConditionAudioEdit::Create, "AdvSceneSwitcher.condition.audio"}); -static std::map audioOutputConditionTypes = { - {AudioOutputCondition::ABOVE, - "AdvSceneSwitcher.condition.audio.state.above"}, - {AudioOutputCondition::BELOW, - "AdvSceneSwitcher.condition.audio.state.below"}, +static std::map 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"}, }; -static std::map audioVolumeConditionTypes = { - {AudioVolumeCondition::ABOVE, - "AdvSceneSwitcher.condition.audio.state.above"}, - {AudioVolumeCondition::EXACT, - "AdvSceneSwitcher.condition.audio.state.exact"}, - {AudioVolumeCondition::BELOW, - "AdvSceneSwitcher.condition.audio.state.below"}, - {AudioVolumeCondition::MUTE, - "AdvSceneSwitcher.condition.audio.state.mute"}, - {AudioVolumeCondition::UNMUTE, - "AdvSceneSwitcher.condition.audio.state.unmute"}, +static std::map + audioOutputConditionTypes = { + {MacroConditionAudio::OutputCondition::ABOVE, + "AdvSceneSwitcher.condition.audio.state.above"}, + {MacroConditionAudio::OutputCondition::BELOW, + "AdvSceneSwitcher.condition.audio.state.below"}, +}; + +static std::map + 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"}, }; MacroConditionAudio::~MacroConditionAudio() @@ -42,11 +54,11 @@ bool MacroConditionAudio::CheckOutputCondition() auto s = obs_weak_source_get_source(_audioSource); switch (_outputCondition) { - case AudioOutputCondition::ABOVE: + case MacroConditionAudio::OutputCondition::ABOVE: // peak will have a value from -60 db to 0 db ret = ((double)_peak + 60) * 1.7 > _volume; break; - case AudioOutputCondition::BELOW: + case MacroConditionAudio::OutputCondition::BELOW: // peak will have a value from -60 db to 0 db ret = ((double)_peak + 60) * 1.7 < _volume; break; @@ -63,24 +75,23 @@ bool MacroConditionAudio::CheckOutputCondition() bool MacroConditionAudio::CheckVolumeCondition() { - bool ret = false; auto s = obs_weak_source_get_source(_audioSource); switch (_volumeCondition) { - case AudioVolumeCondition::ABOVE: + case MacroConditionAudio::VolumeCondition::ABOVE: ret = obs_source_get_volume(s) > _volume / 100.f; break; - case AudioVolumeCondition::EXACT: + case MacroConditionAudio::VolumeCondition::EXACT: ret = obs_source_get_volume(s) == _volume / 100.f; break; - case AudioVolumeCondition::BELOW: + case MacroConditionAudio::VolumeCondition::BELOW: ret = obs_source_get_volume(s) < _volume / 100.f; break; - case AudioVolumeCondition::MUTE: + case MacroConditionAudio::VolumeCondition::MUTE: ret = obs_source_muted(s); break; - case AudioVolumeCondition::UNMUTE: + case MacroConditionAudio::VolumeCondition::UNMUTE: ret = !obs_source_muted(s); break; default: @@ -91,13 +102,28 @@ bool MacroConditionAudio::CheckVolumeCondition() return ret; } +bool MacroConditionAudio::CheckSyncOffset() +{ + if (!_audioSource) { + return false; + } + + bool ret = false; + auto s = obs_weak_source_get_source(_audioSource); + ret = (obs_source_get_sync_offset(s) / nsPerMs) == _syncOffset; + obs_source_release(s); + return ret; +} + bool MacroConditionAudio::CheckCondition() { switch (_checkType) { - case AudioConditionCheckType::OUTPUT_VOLUME: + case MacroConditionAudio::Type::OUTPUT_VOLUME: return CheckOutputCondition(); - case AudioConditionCheckType::CONFIGURED_VOLUME: + case MacroConditionAudio::Type::CONFIGURED_VOLUME: return CheckVolumeCondition(); + case MacroConditionAudio::Type::SYNC_OFFSET: + return CheckSyncOffset(); default: break; } @@ -110,6 +136,7 @@ bool MacroConditionAudio::Save(obs_data_t *obj) obs_data_set_string(obj, "audioSource", GetWeakSourceName(_audioSource).c_str()); obs_data_set_int(obj, "volume", _volume); + obs_data_set_int(obj, "syncOffset", _syncOffset); obs_data_set_int(obj, "checkType", static_cast(_checkType)); obs_data_set_int(obj, "outputCondition", static_cast(_outputCondition)); @@ -135,65 +162,18 @@ obs_volmeter_t *AddVolmeterToSource(MacroConditionAudio *entry, return volmeter; } -// TODO: Remove in future version -static void convertOldSettingsFormat(obs_data_t *obj) -{ - auto oldCond = static_cast( - obs_data_get_int(obj, "condition")); - switch (oldCond) { - case AudioOutputCondition::ABOVE: - obs_data_set_int( - obj, "checkType", - static_cast( - AudioConditionCheckType::OUTPUT_VOLUME)); - obs_data_set_int(obj, "outputCondition", - static_cast(AudioOutputCondition::ABOVE)); - break; - case AudioOutputCondition::BELOW: - obs_data_set_int( - obj, "checkType", - static_cast( - AudioConditionCheckType::OUTPUT_VOLUME)); - obs_data_set_int(obj, "outputCondition", - static_cast(AudioOutputCondition::BELOW)); - break; - case AudioOutputCondition::MUTE: - obs_data_set_int( - obj, "checkType", - static_cast( - AudioConditionCheckType::CONFIGURED_VOLUME)); - obs_data_set_int(obj, "volumeCondition", - static_cast(AudioVolumeCondition::MUTE)); - break; - case AudioOutputCondition::UNMUTE: - obs_data_set_int( - obj, "checkType", - static_cast( - AudioConditionCheckType::CONFIGURED_VOLUME)); - obs_data_set_int( - obj, "volumeCondition", - static_cast(AudioVolumeCondition::UNMUTE)); - break; - default: - break; - } -} - bool MacroConditionAudio::Load(obs_data_t *obj) { - if (!obs_data_has_user_value(obj, "checkType")) { - convertOldSettingsFormat(obj); - } - MacroCondition::Load(obj); const char *audioSourceName = obs_data_get_string(obj, "audioSource"); _audioSource = GetWeakSourceByName(audioSourceName); _volume = obs_data_get_int(obj, "volume"); - _checkType = static_cast( + _syncOffset = obs_data_get_int(obj, "syncOffset"); + _checkType = static_cast( obs_data_get_int(obj, "checkType")); - _outputCondition = static_cast( + _outputCondition = static_cast( obs_data_get_int(obj, "outputCondition")); - _volumeCondition = static_cast( + _volumeCondition = static_cast( obs_data_get_int(obj, "volumeCondition")); _volmeter = AddVolmeterToSource(this, _audioSource); return true; @@ -234,10 +214,10 @@ void MacroConditionAudio::ResetVolmeter() static inline void populateCheckTypes(QComboBox *list) { - list->addItem(obs_module_text( - "AdvSceneSwitcher.condition.audio.type.output")); - list->addItem(obs_module_text( - "AdvSceneSwitcher.condition.audio.type.volume")); + list->clear(); + for (auto entry : checkTypes) { + list->addItem(obs_module_text(entry.second.c_str())); + } } static inline void populateOutputConditionSelection(QComboBox *list) @@ -258,21 +238,27 @@ static inline void populateVolumeConditionSelection(QComboBox *list) MacroConditionAudioEdit::MacroConditionAudioEdit( QWidget *parent, std::shared_ptr entryData) - : QWidget(parent) + : QWidget(parent), + _checkTypes(new QComboBox()), + _audioSources(new QComboBox()), + _condition(new QComboBox()), + _volume(new QSpinBox()), + _syncOffset(new QSpinBox()) { - _checkTypes = new QComboBox(); - _audioSources = new QComboBox(); - _condition = new QComboBox(); - _volume = new QSpinBox(); - _volume->setSuffix("%"); _volume->setMaximum(100); _volume->setMinimum(0); + _syncOffset->setMinimum(-950); + _syncOffset->setMaximum(20000); + _syncOffset->setSuffix("ms"); + QWidget::connect(_checkTypes, SIGNAL(currentIndexChanged(int)), this, SLOT(CheckTypeChanged(int))); QWidget::connect(_volume, SIGNAL(valueChanged(int)), this, SLOT(VolumeThresholdChanged(int))); + QWidget::connect(_syncOffset, SIGNAL(valueChanged(int)), this, + SLOT(SyncOffsetChanged(int))); QWidget::connect(_condition, SIGNAL(currentIndexChanged(int)), this, SLOT(ConditionChanged(int))); QWidget::connect(_audioSources, @@ -287,6 +273,7 @@ MacroConditionAudioEdit::MacroConditionAudioEdit( {"{{checkType}}", _checkTypes}, {"{{audioSources}}", _audioSources}, {"{{volume}}", _volume}, + {"{{syncOffset}}", _syncOffset}, {"{{condition}}", _condition}, }; placeWidgets(obs_module_text("AdvSceneSwitcher.condition.audio.entry"), @@ -348,6 +335,16 @@ void MacroConditionAudioEdit::VolumeThresholdChanged(int vol) _entryData->_volume = vol; } +void MacroConditionAudioEdit::SyncOffsetChanged(int value) +{ + if (_loading || !_entryData) { + return; + } + + std::lock_guard lock(switcher->m); + _entryData->_syncOffset = value; +} + void MacroConditionAudioEdit::ConditionChanged(int cond) { if (_loading || !_entryData) { @@ -355,12 +352,13 @@ void MacroConditionAudioEdit::ConditionChanged(int cond) } std::lock_guard lock(switcher->m); - if (_entryData->_checkType == AudioConditionCheckType::OUTPUT_VOLUME) { + if (_entryData->_checkType == + MacroConditionAudio::Type::OUTPUT_VOLUME) { _entryData->_outputCondition = - static_cast(cond); + static_cast(cond); } else { _entryData->_volumeCondition = - static_cast(cond); + static_cast(cond); } SetWidgetVisibility(); } @@ -372,12 +370,14 @@ void MacroConditionAudioEdit::CheckTypeChanged(int cond) } std::lock_guard lock(switcher->m); - _entryData->_checkType = static_cast(cond); + _entryData->_checkType = static_cast(cond); const QSignalBlocker b(_condition); - if (_entryData->_checkType == AudioConditionCheckType::OUTPUT_VOLUME) { + if (_entryData->_checkType == + MacroConditionAudio::Type::OUTPUT_VOLUME) { populateOutputConditionSelection(_condition); - } else { + } else if (_entryData->_checkType == + MacroConditionAudio::Type::CONFIGURED_VOLUME) { populateVolumeConditionSelection(_condition); } SetWidgetVisibility(); @@ -392,12 +392,15 @@ void MacroConditionAudioEdit::UpdateEntryData() _audioSources->setCurrentText( GetWeakSourceName(_entryData->_audioSource).c_str()); _volume->setValue(_entryData->_volume); + _syncOffset->setValue(_entryData->_syncOffset); _checkTypes->setCurrentIndex(static_cast(_entryData->_checkType)); - if (_entryData->_checkType == AudioConditionCheckType::OUTPUT_VOLUME) { + if (_entryData->_checkType == + MacroConditionAudio::Type::OUTPUT_VOLUME) { populateOutputConditionSelection(_condition); _condition->setCurrentIndex( static_cast(_entryData->_outputCondition)); - } else { + } else if (_entryData->_checkType == + MacroConditionAudio::Type::CONFIGURED_VOLUME) { populateVolumeConditionSelection(_condition); _condition->setCurrentIndex( static_cast(_entryData->_volumeCondition)); @@ -414,13 +417,23 @@ void MacroConditionAudioEdit::SetWidgetVisibility() _volume->setVisible( _entryData->_checkType == - AudioConditionCheckType::OUTPUT_VOLUME || + MacroConditionAudio::Type::OUTPUT_VOLUME || (_entryData->_checkType == - AudioConditionCheckType::CONFIGURED_VOLUME && - (_entryData->_volumeCondition == AudioVolumeCondition::ABOVE || - _entryData->_volumeCondition == AudioVolumeCondition::EXACT || - _entryData->_volumeCondition == AudioVolumeCondition::BELOW))); + MacroConditionAudio::Type::CONFIGURED_VOLUME && + (_entryData->_volumeCondition == + MacroConditionAudio::VolumeCondition::ABOVE || + _entryData->_volumeCondition == + MacroConditionAudio::VolumeCondition::EXACT || + _entryData->_volumeCondition == + MacroConditionAudio::VolumeCondition::BELOW))); + _condition->setVisible( + _entryData->_checkType == + MacroConditionAudio::Type::OUTPUT_VOLUME || + _entryData->_checkType == + MacroConditionAudio::Type::CONFIGURED_VOLUME); + _syncOffset->setVisible(_entryData->_checkType == + MacroConditionAudio::Type::SYNC_OFFSET); _volMeter->setVisible(_entryData->_checkType == - AudioConditionCheckType::OUTPUT_VOLUME); + MacroConditionAudio::Type::OUTPUT_VOLUME); adjustSize(); } diff --git a/src/macro-core/macro-condition-audio.hpp b/src/macro-core/macro-condition-audio.hpp index 48e66172..35e4f1e1 100644 --- a/src/macro-core/macro-condition-audio.hpp +++ b/src/macro-core/macro-condition-audio.hpp @@ -6,27 +6,6 @@ #include #include -enum class AudioConditionCheckType { - OUTPUT_VOLUME, - CONFIGURED_VOLUME, -}; - -enum class AudioOutputCondition { - ABOVE, - BELOW, - // TODO: To be removed - MUTE, - UNMUTE, -}; - -enum class AudioVolumeCondition { - ABOVE, - EXACT, - BELOW, - MUTE, - UNMUTE, -}; - class MacroConditionAudio : public MacroCondition { public: MacroConditionAudio(Macro *m) : MacroCondition(m) {} @@ -46,17 +25,37 @@ public: const float inputPeak[MAX_AUDIO_CHANNELS]); void ResetVolmeter(); + enum class Type { + OUTPUT_VOLUME, + CONFIGURED_VOLUME, + SYNC_OFFSET, + }; + + enum class OutputCondition { + ABOVE, + BELOW, + }; + + enum class VolumeCondition { + ABOVE, + EXACT, + BELOW, + MUTE, + UNMUTE, + }; + OBSWeakSource _audioSource; int _volume = 0; - AudioConditionCheckType _checkType = - AudioConditionCheckType::OUTPUT_VOLUME; - AudioOutputCondition _outputCondition = AudioOutputCondition::ABOVE; - AudioVolumeCondition _volumeCondition = AudioVolumeCondition::ABOVE; + int64_t _syncOffset = 0; + Type _checkType = Type::OUTPUT_VOLUME; + OutputCondition _outputCondition = OutputCondition::ABOVE; + VolumeCondition _volumeCondition = VolumeCondition::ABOVE; obs_volmeter_t *_volmeter = nullptr; private: bool CheckOutputCondition(); bool CheckVolumeCondition(); + bool CheckSyncOffset(); float _peak = -std::numeric_limits::infinity(); static bool _registered; @@ -85,6 +84,7 @@ private slots: void VolumeThresholdChanged(int vol); void ConditionChanged(int cond); void CheckTypeChanged(int cond); + void SyncOffsetChanged(int value); signals: void HeaderInfoChanged(const QString &); @@ -94,6 +94,7 @@ protected: QComboBox *_audioSources; QComboBox *_condition; QSpinBox *_volume; + QSpinBox *_syncOffset; VolControl *_volMeter = nullptr; std::shared_ptr _entryData;