mirror of
https://github.com/WarmUpTill/SceneSwitcher.git
synced 2026-04-17 15:21:39 -05:00
Add action to play sound
This commit is contained in:
parent
a01d26e25d
commit
1ace185a57
|
|
@ -876,6 +876,15 @@ AdvSceneSwitcher.action.audio.fade.rate="{{fade}}Fade{{fadeTypes}}{{rate}}per se
|
|||
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}}{{volumeDB}}{{percentDBToggle}}{{syncOffset}}{{monitorTypes}}{{track}}"
|
||||
AdvSceneSwitcher.action.playAudio="Play Audio File"
|
||||
AdvSceneSwitcher.action.playAudio.file.browse="Browse for audio file"
|
||||
AdvSceneSwitcher.action.playAudio.layout.volume="Volume{{volumeDB}}"
|
||||
AdvSceneSwitcher.action.playAudio.layout.monitor="Monitoring{{monitorTypes}}"
|
||||
AdvSceneSwitcher.action.playAudio.wait="Wait for playback to complete"
|
||||
AdvSceneSwitcher.action.playAudio.monitorUnavailable="Audio monitoring not available"
|
||||
AdvSceneSwitcher.action.playAudio.tracks="Tracks"
|
||||
AdvSceneSwitcher.action.playAudio.layout.startOffset="{{useStartOffset}}Start at{{startOffset}}"
|
||||
AdvSceneSwitcher.action.playAudio.layout.playbackDuration="{{useDuration}}Play for{{playbackDuration}}(0 = until end)"
|
||||
AdvSceneSwitcher.action.recording="Recording"
|
||||
AdvSceneSwitcher.action.recording.type.stop="Stop recording"
|
||||
AdvSceneSwitcher.action.recording.type.start="Start recording"
|
||||
|
|
|
|||
|
|
@ -25,6 +25,8 @@ target_sources(
|
|||
macro-action-obs-settings.hpp
|
||||
macro-action-osc.cpp
|
||||
macro-action-osc.hpp
|
||||
macro-action-play-audio.cpp
|
||||
macro-action-play-audio.hpp
|
||||
macro-action-plugin-state.cpp
|
||||
macro-action-plugin-state.hpp
|
||||
macro-action-profile.cpp
|
||||
|
|
|
|||
443
plugins/base/macro-action-play-audio.cpp
Normal file
443
plugins/base/macro-action-play-audio.cpp
Normal file
|
|
@ -0,0 +1,443 @@
|
|||
#include "macro-action-play-audio.hpp"
|
||||
#include "audio-helpers.hpp"
|
||||
#include "layout-helpers.hpp"
|
||||
#include "macro-helpers.hpp"
|
||||
#include "sync-helpers.hpp"
|
||||
|
||||
#include <chrono>
|
||||
#include <QFileInfo>
|
||||
#include <QLabel>
|
||||
|
||||
namespace advss {
|
||||
|
||||
// Use a high output channel index that is unlikely to be claimed by OBS or
|
||||
// other plugins. OBS supports channels 0-63; channel 0 is the main scene.
|
||||
static constexpr uint32_t kPlaybackOutputChannel = 63;
|
||||
|
||||
const std::string MacroActionPlayAudio::id = "play_audio";
|
||||
|
||||
bool MacroActionPlayAudio::_registered = MacroActionFactory::Register(
|
||||
MacroActionPlayAudio::id,
|
||||
{MacroActionPlayAudio::Create, MacroActionPlayAudioEdit::Create,
|
||||
"AdvSceneSwitcher.action.playAudio"});
|
||||
|
||||
static void deactivatePlayback(obs_source_t *source, bool wantsOutput)
|
||||
{
|
||||
if (wantsOutput) {
|
||||
obs_set_output_source(kPlaybackOutputChannel, nullptr);
|
||||
} else {
|
||||
obs_source_dec_active(source);
|
||||
}
|
||||
}
|
||||
|
||||
static void waitForPlaybackToEnd(Macro *macro, obs_source_t *source,
|
||||
int64_t maxMs = 0)
|
||||
{
|
||||
using namespace std::chrono_literals;
|
||||
|
||||
std::unique_lock<std::mutex> lock(*GetMutex());
|
||||
SetMacroAbortWait(false);
|
||||
|
||||
// The media source needs time to open and decode before reaching
|
||||
// PLAYING state. Poll until it starts (or the macro is stopped).
|
||||
while (!MacroWaitShouldAbort() && !MacroIsStopped(macro)) {
|
||||
if (obs_source_media_get_state(source) ==
|
||||
OBS_MEDIA_STATE_PLAYING) {
|
||||
break;
|
||||
}
|
||||
GetMacroWaitCV().wait_for(lock, 10ms);
|
||||
}
|
||||
|
||||
// Now wait for playback to end. Require two consecutive non-playing
|
||||
// samples to avoid false positives on brief state transitions.
|
||||
// If maxMs > 0, also stop once that many milliseconds have elapsed.
|
||||
const auto playbackStart = std::chrono::steady_clock::now();
|
||||
static const int kStopThreshold = 2;
|
||||
int stoppedCount = 0;
|
||||
while (!MacroWaitShouldAbort() && !MacroIsStopped(macro)) {
|
||||
if (maxMs > 0) {
|
||||
const auto elapsed =
|
||||
std::chrono::duration_cast<
|
||||
std::chrono::milliseconds>(
|
||||
std::chrono::steady_clock::now() -
|
||||
playbackStart)
|
||||
.count();
|
||||
if (elapsed >= maxMs) {
|
||||
obs_source_media_stop(source);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (obs_source_media_get_state(source) !=
|
||||
OBS_MEDIA_STATE_PLAYING) {
|
||||
if (++stoppedCount >= kStopThreshold) {
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
stoppedCount = 0;
|
||||
}
|
||||
GetMacroWaitCV().wait_for(lock, 10ms);
|
||||
}
|
||||
}
|
||||
|
||||
bool MacroActionPlayAudio::PerformAction()
|
||||
{
|
||||
std::string path = _filePath;
|
||||
if (path.empty()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!QFileInfo::exists(QString::fromStdString(path))) {
|
||||
blog(LOG_WARNING, "audio file not found: \"%s\"", path.c_str());
|
||||
return true;
|
||||
}
|
||||
|
||||
OBSDataAutoRelease settings = obs_data_create();
|
||||
obs_data_set_string(settings, "local_file", path.c_str());
|
||||
obs_data_set_bool(settings, "is_local_file", true);
|
||||
obs_data_set_bool(settings, "looping", false);
|
||||
// Disable automatic restart on activate so we control start explicitly.
|
||||
obs_data_set_bool(settings, "restart_on_activate", false);
|
||||
obs_data_set_bool(settings, "close_when_inactive", true);
|
||||
|
||||
OBSSourceAutoRelease source = obs_source_create_private(
|
||||
"ffmpeg_source", "advss_play_audio", settings);
|
||||
|
||||
if (!source) {
|
||||
blog(LOG_WARNING,
|
||||
"Failed to create ffmpeg_source for audio playback of \"%s\"",
|
||||
path.c_str());
|
||||
return true;
|
||||
}
|
||||
|
||||
const float vol =
|
||||
DecibelToPercent(static_cast<float>(_volumeDB.GetValue()));
|
||||
obs_source_set_volume(source, vol);
|
||||
obs_source_set_monitoring_type(source, _monitorType);
|
||||
|
||||
// Fall back to monitor-only if all output tracks are deselected —
|
||||
// there is no point routing through the output channel if the mixer
|
||||
// mask would silence every track.
|
||||
const bool wantsOutput =
|
||||
(_monitorType != OBS_MONITORING_TYPE_MONITOR_ONLY) &&
|
||||
(_audioMixers != 0);
|
||||
if (!wantsOutput &&
|
||||
_monitorType == OBS_MONITORING_TYPE_MONITOR_AND_OUTPUT) {
|
||||
obs_source_set_monitoring_type(
|
||||
source, OBS_MONITORING_TYPE_MONITOR_ONLY);
|
||||
}
|
||||
|
||||
if (wantsOutput) {
|
||||
obs_source_set_audio_mixers(source, _audioMixers);
|
||||
// Route through a private scene so we can position the scene
|
||||
// item far off-screen. This keeps the item "visible" (so audio
|
||||
// is still mixed) while ensuring its video never intersects the
|
||||
// output frame.
|
||||
OBSSceneAutoRelease audioScene =
|
||||
obs_scene_create_private("advss_audio_scene");
|
||||
obs_sceneitem_t *item = obs_scene_add(audioScene, source);
|
||||
if (item) {
|
||||
struct vec2 pos = {-99999.0f, -99999.0f};
|
||||
obs_sceneitem_set_pos(item, &pos);
|
||||
}
|
||||
obs_set_output_source(kPlaybackOutputChannel,
|
||||
obs_scene_get_source(audioScene));
|
||||
// audioScene released here; the output channel holds the
|
||||
// remaining reference and keeps the scene alive.
|
||||
} else {
|
||||
obs_source_set_audio_mixers(source, 0);
|
||||
obs_source_inc_active(source);
|
||||
}
|
||||
|
||||
if (_useStartOffset && _startOffset.Milliseconds() > 0) {
|
||||
obs_source_media_set_time(source, _startOffset.Milliseconds());
|
||||
}
|
||||
|
||||
obs_source_media_play_pause(source, false);
|
||||
|
||||
const int64_t maxMs = _useDuration ? _playbackDuration.Milliseconds()
|
||||
: 0;
|
||||
|
||||
if (_waitForCompletion) {
|
||||
SetMacroAbortWait(false);
|
||||
waitForPlaybackToEnd(GetMacro(), source, maxMs);
|
||||
deactivatePlayback(source, wantsOutput);
|
||||
return true;
|
||||
}
|
||||
|
||||
// Keep the source alive in a background thread that cleans up
|
||||
// once playback finishes. Grab an extra strong reference so the
|
||||
// source survives beyond this stack frame.
|
||||
auto rawSource = obs_source_get_ref(source);
|
||||
auto macro = GetMacro();
|
||||
|
||||
std::thread cleanupThread([rawSource, wantsOutput, macro, maxMs]() {
|
||||
waitForPlaybackToEnd(macro, rawSource, maxMs);
|
||||
deactivatePlayback(rawSource, wantsOutput);
|
||||
obs_source_release(rawSource);
|
||||
});
|
||||
AddMacroHelperThread(macro, std::move(cleanupThread));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void MacroActionPlayAudio::LogAction() const
|
||||
{
|
||||
ablog(LOG_INFO,
|
||||
"playing audio file \"%s\" at %.1f dB (monitoring type %d, wait %d)",
|
||||
_filePath.UnresolvedValue().c_str(),
|
||||
static_cast<double>(_volumeDB.GetFixedValue()), _monitorType,
|
||||
(int)_waitForCompletion);
|
||||
}
|
||||
|
||||
bool MacroActionPlayAudio::Save(obs_data_t *obj) const
|
||||
{
|
||||
MacroAction::Save(obj);
|
||||
_filePath.Save(obj, "filePath");
|
||||
_volumeDB.Save(obj, "volumeDB");
|
||||
obs_data_set_int(obj, "monitorType", _monitorType);
|
||||
obs_data_set_int(obj, "audioMixers", _audioMixers);
|
||||
obs_data_set_bool(obj, "useStartOffset", _useStartOffset);
|
||||
_startOffset.Save(obj, "startOffset");
|
||||
obs_data_set_bool(obj, "useDuration", _useDuration);
|
||||
_playbackDuration.Save(obj, "playbackDuration");
|
||||
obs_data_set_bool(obj, "waitForCompletion", _waitForCompletion);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool MacroActionPlayAudio::Load(obs_data_t *obj)
|
||||
{
|
||||
MacroAction::Load(obj);
|
||||
_filePath.Load(obj, "filePath");
|
||||
_volumeDB.Load(obj, "volumeDB");
|
||||
_monitorType = static_cast<obs_monitoring_type>(
|
||||
obs_data_get_int(obj, "monitorType"));
|
||||
_audioMixers =
|
||||
static_cast<uint32_t>(obs_data_get_int(obj, "audioMixers"));
|
||||
_useStartOffset = obs_data_get_bool(obj, "useStartOffset");
|
||||
_startOffset.Load(obj, "startOffset");
|
||||
_useDuration = obs_data_get_bool(obj, "useDuration");
|
||||
_playbackDuration.Load(obj, "playbackDuration");
|
||||
_waitForCompletion = obs_data_get_bool(obj, "waitForCompletion");
|
||||
return true;
|
||||
}
|
||||
|
||||
std::string MacroActionPlayAudio::GetShortDesc() const
|
||||
{
|
||||
return _filePath.UnresolvedValue();
|
||||
}
|
||||
|
||||
std::shared_ptr<MacroAction> MacroActionPlayAudio::Create(Macro *m)
|
||||
{
|
||||
return std::make_shared<MacroActionPlayAudio>(m);
|
||||
}
|
||||
|
||||
std::shared_ptr<MacroAction> MacroActionPlayAudio::Copy() const
|
||||
{
|
||||
return std::make_shared<MacroActionPlayAudio>(*this);
|
||||
}
|
||||
|
||||
void MacroActionPlayAudio::ResolveVariablesToFixedValues()
|
||||
{
|
||||
_filePath.ResolveVariables();
|
||||
_volumeDB.ResolveVariables();
|
||||
_startOffset.ResolveVariables();
|
||||
_playbackDuration.ResolveVariables();
|
||||
}
|
||||
|
||||
MacroActionPlayAudioEdit::MacroActionPlayAudioEdit(
|
||||
QWidget *parent, std::shared_ptr<MacroActionPlayAudio> entryData)
|
||||
: QWidget(parent),
|
||||
_filePath(new FileSelection(
|
||||
FileSelection::Type::READ, this,
|
||||
obs_module_text(
|
||||
"AdvSceneSwitcher.action.playAudio.file.browse"))),
|
||||
_volumeDB(new VariableDoubleSpinBox),
|
||||
_monitorTypes(new QComboBox),
|
||||
_tracksContainer(new QWidget),
|
||||
_useStartOffset(new QCheckBox(this)),
|
||||
_startOffset(new DurationSelection(this, true, 0.0)),
|
||||
_useDuration(new QCheckBox(this)),
|
||||
_playbackDuration(new DurationSelection(this, true, 0.0)),
|
||||
_waitForCompletion(new QCheckBox(
|
||||
obs_module_text("AdvSceneSwitcher.action.playAudio.wait")))
|
||||
{
|
||||
_volumeDB->setMinimum(-100.0);
|
||||
_volumeDB->setMaximum(0.0);
|
||||
_volumeDB->setSuffix("dB");
|
||||
_volumeDB->setSpecialValueText("-inf");
|
||||
|
||||
if (obs_audio_monitoring_available()) {
|
||||
PopulateMonitorTypeSelection(_monitorTypes);
|
||||
} else {
|
||||
_monitorTypes->addItem(obs_module_text(
|
||||
"AdvSceneSwitcher.action.playAudio.monitorUnavailable"));
|
||||
_monitorTypes->setEnabled(false);
|
||||
}
|
||||
|
||||
for (int i = 0; i < 6; ++i) {
|
||||
_tracks[i] = new QCheckBox(QString::number(i + 1));
|
||||
QWidget::connect(_tracks[i], SIGNAL(stateChanged(int)), this,
|
||||
SLOT(TrackChanged()));
|
||||
}
|
||||
|
||||
QWidget::connect(_filePath, SIGNAL(PathChanged(const QString &)), this,
|
||||
SLOT(FilePathChanged(const QString &)));
|
||||
QWidget::connect(
|
||||
_volumeDB,
|
||||
SIGNAL(NumberVariableChanged(const NumberVariable<double> &)),
|
||||
this, SLOT(VolumeDBChanged(const NumberVariable<double> &)));
|
||||
QWidget::connect(_monitorTypes, SIGNAL(currentIndexChanged(int)), this,
|
||||
SLOT(MonitorTypeChanged(int)));
|
||||
QWidget::connect(_useStartOffset, SIGNAL(stateChanged(int)), this,
|
||||
SLOT(UseStartOffsetChanged(int)));
|
||||
QWidget::connect(_useDuration, SIGNAL(stateChanged(int)), this,
|
||||
SLOT(UseDurationChanged(int)));
|
||||
QWidget::connect(_startOffset,
|
||||
SIGNAL(DurationChanged(const Duration &)), this,
|
||||
SLOT(StartOffsetChanged(const Duration &)));
|
||||
QWidget::connect(_playbackDuration,
|
||||
SIGNAL(DurationChanged(const Duration &)), this,
|
||||
SLOT(PlaybackDurationChanged(const Duration &)));
|
||||
QWidget::connect(_waitForCompletion, SIGNAL(stateChanged(int)), this,
|
||||
SLOT(WaitChanged(int)));
|
||||
|
||||
auto tracksLayout = new QHBoxLayout;
|
||||
tracksLayout->setContentsMargins(0, 0, 0, 0);
|
||||
tracksLayout->addWidget(new QLabel(
|
||||
obs_module_text("AdvSceneSwitcher.action.playAudio.tracks")));
|
||||
for (auto *cb : _tracks) {
|
||||
tracksLayout->addWidget(cb);
|
||||
}
|
||||
tracksLayout->addStretch();
|
||||
_tracksContainer->setLayout(tracksLayout);
|
||||
|
||||
auto volumeLayout = new QHBoxLayout;
|
||||
PlaceWidgets(obs_module_text(
|
||||
"AdvSceneSwitcher.action.playAudio.layout.volume"),
|
||||
volumeLayout, {{"{{volumeDB}}", _volumeDB}});
|
||||
auto monitorLayout = new QHBoxLayout;
|
||||
PlaceWidgets(
|
||||
obs_module_text(
|
||||
"AdvSceneSwitcher.action.playAudio.layout.monitor"),
|
||||
monitorLayout, {{"{{monitorTypes}}", _monitorTypes}});
|
||||
auto startOffsetLayout = new QHBoxLayout;
|
||||
PlaceWidgets(
|
||||
obs_module_text(
|
||||
"AdvSceneSwitcher.action.playAudio.layout.startOffset"),
|
||||
startOffsetLayout,
|
||||
{{"{{useStartOffset}}", _useStartOffset},
|
||||
{"{{startOffset}}", _startOffset}});
|
||||
auto playbackDurationLayout = new QHBoxLayout;
|
||||
PlaceWidgets(
|
||||
obs_module_text(
|
||||
"AdvSceneSwitcher.action.playAudio.layout.playbackDuration"),
|
||||
playbackDurationLayout,
|
||||
{{"{{useDuration}}", _useDuration},
|
||||
{"{{playbackDuration}}", _playbackDuration}});
|
||||
|
||||
auto mainLayout = new QVBoxLayout;
|
||||
mainLayout->addWidget(_filePath);
|
||||
mainLayout->addLayout(volumeLayout);
|
||||
mainLayout->addLayout(monitorLayout);
|
||||
mainLayout->addWidget(_tracksContainer);
|
||||
mainLayout->addLayout(startOffsetLayout);
|
||||
mainLayout->addLayout(playbackDurationLayout);
|
||||
mainLayout->addWidget(_waitForCompletion);
|
||||
setLayout(mainLayout);
|
||||
|
||||
_entryData = entryData;
|
||||
UpdateEntryData();
|
||||
_loading = false;
|
||||
}
|
||||
|
||||
void MacroActionPlayAudioEdit::UpdateEntryData()
|
||||
{
|
||||
if (!_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
_filePath->SetPath(_entryData->_filePath);
|
||||
_volumeDB->SetValue(_entryData->_volumeDB);
|
||||
_monitorTypes->setCurrentIndex(
|
||||
static_cast<int>(_entryData->_monitorType));
|
||||
for (int i = 0; i < 6; ++i) {
|
||||
_tracks[i]->setChecked(_entryData->_audioMixers & (1u << i));
|
||||
}
|
||||
_tracksContainer->setVisible(_entryData->_monitorType !=
|
||||
OBS_MONITORING_TYPE_MONITOR_ONLY);
|
||||
_useStartOffset->setChecked(_entryData->_useStartOffset);
|
||||
_startOffset->SetDuration(_entryData->_startOffset);
|
||||
_startOffset->setEnabled(_entryData->_useStartOffset);
|
||||
_useDuration->setChecked(_entryData->_useDuration);
|
||||
_playbackDuration->SetDuration(_entryData->_playbackDuration);
|
||||
_playbackDuration->setEnabled(_entryData->_useDuration);
|
||||
_waitForCompletion->setChecked(_entryData->_waitForCompletion);
|
||||
}
|
||||
|
||||
void MacroActionPlayAudioEdit::FilePathChanged(const QString &path)
|
||||
{
|
||||
GUARD_LOADING_AND_LOCK();
|
||||
_entryData->_filePath = path.toStdString();
|
||||
emit HeaderInfoChanged(
|
||||
QString::fromStdString(_entryData->GetShortDesc()));
|
||||
}
|
||||
|
||||
void MacroActionPlayAudioEdit::VolumeDBChanged(
|
||||
const NumberVariable<double> &value)
|
||||
{
|
||||
GUARD_LOADING_AND_LOCK();
|
||||
_entryData->_volumeDB = value;
|
||||
}
|
||||
|
||||
void MacroActionPlayAudioEdit::MonitorTypeChanged(int value)
|
||||
{
|
||||
GUARD_LOADING_AND_LOCK();
|
||||
_entryData->_monitorType = static_cast<obs_monitoring_type>(value);
|
||||
_tracksContainer->setVisible(value != OBS_MONITORING_TYPE_MONITOR_ONLY);
|
||||
}
|
||||
|
||||
void MacroActionPlayAudioEdit::TrackChanged()
|
||||
{
|
||||
GUARD_LOADING_AND_LOCK();
|
||||
uint32_t mixers = 0;
|
||||
for (int i = 0; i < 6; ++i) {
|
||||
if (_tracks[i]->isChecked()) {
|
||||
mixers |= (1u << i);
|
||||
}
|
||||
}
|
||||
_entryData->_audioMixers = mixers;
|
||||
}
|
||||
|
||||
void MacroActionPlayAudioEdit::UseStartOffsetChanged(int value)
|
||||
{
|
||||
GUARD_LOADING_AND_LOCK();
|
||||
_entryData->_useStartOffset = value;
|
||||
_startOffset->setEnabled(_entryData->_useStartOffset);
|
||||
}
|
||||
|
||||
void MacroActionPlayAudioEdit::StartOffsetChanged(const Duration &value)
|
||||
{
|
||||
GUARD_LOADING_AND_LOCK();
|
||||
_entryData->_startOffset = value;
|
||||
}
|
||||
|
||||
void MacroActionPlayAudioEdit::UseDurationChanged(int value)
|
||||
{
|
||||
GUARD_LOADING_AND_LOCK();
|
||||
_entryData->_useDuration = value;
|
||||
_playbackDuration->setEnabled(_entryData->_useDuration);
|
||||
}
|
||||
|
||||
void MacroActionPlayAudioEdit::PlaybackDurationChanged(const Duration &value)
|
||||
{
|
||||
GUARD_LOADING_AND_LOCK();
|
||||
_entryData->_playbackDuration = value;
|
||||
}
|
||||
|
||||
void MacroActionPlayAudioEdit::WaitChanged(int value)
|
||||
{
|
||||
GUARD_LOADING_AND_LOCK();
|
||||
_entryData->_waitForCompletion = value;
|
||||
}
|
||||
|
||||
} // namespace advss
|
||||
91
plugins/base/macro-action-play-audio.hpp
Normal file
91
plugins/base/macro-action-play-audio.hpp
Normal file
|
|
@ -0,0 +1,91 @@
|
|||
#pragma once
|
||||
#include "macro-action-edit.hpp"
|
||||
#include "duration-control.hpp"
|
||||
#include "file-selection.hpp"
|
||||
#include "variable-spinbox.hpp"
|
||||
|
||||
#include <obs.hpp>
|
||||
|
||||
#include <array>
|
||||
|
||||
#include <QCheckBox>
|
||||
#include <QComboBox>
|
||||
#include <QWidget>
|
||||
|
||||
namespace advss {
|
||||
|
||||
class MacroActionPlayAudio : public MacroAction {
|
||||
public:
|
||||
MacroActionPlayAudio(Macro *m) : MacroAction(m) {}
|
||||
bool PerformAction();
|
||||
void LogAction() const;
|
||||
bool Save(obs_data_t *obj) const;
|
||||
bool Load(obs_data_t *obj);
|
||||
std::string GetShortDesc() const;
|
||||
std::string GetId() const { return id; }
|
||||
static std::shared_ptr<MacroAction> Create(Macro *m);
|
||||
std::shared_ptr<MacroAction> Copy() const;
|
||||
void ResolveVariablesToFixedValues();
|
||||
|
||||
StringVariable _filePath =
|
||||
obs_module_text("AdvSceneSwitcher.enterPath");
|
||||
DoubleVariable _volumeDB = 0.0;
|
||||
obs_monitoring_type _monitorType = OBS_MONITORING_TYPE_MONITOR_ONLY;
|
||||
uint32_t _audioMixers = 0x3F; // tracks 1-6, all on by default
|
||||
bool _useStartOffset = false;
|
||||
Duration _startOffset;
|
||||
bool _useDuration = false;
|
||||
Duration _playbackDuration;
|
||||
bool _waitForCompletion = false;
|
||||
|
||||
static bool _registered;
|
||||
static const std::string id;
|
||||
};
|
||||
|
||||
class MacroActionPlayAudioEdit : public QWidget {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
MacroActionPlayAudioEdit(
|
||||
QWidget *parent,
|
||||
std::shared_ptr<MacroActionPlayAudio> entryData = nullptr);
|
||||
void UpdateEntryData();
|
||||
static QWidget *Create(QWidget *parent,
|
||||
std::shared_ptr<MacroAction> action)
|
||||
{
|
||||
return new MacroActionPlayAudioEdit(
|
||||
parent, std::dynamic_pointer_cast<MacroActionPlayAudio>(
|
||||
action));
|
||||
}
|
||||
|
||||
private slots:
|
||||
void FilePathChanged(const QString &path);
|
||||
void VolumeDBChanged(const NumberVariable<double> &value);
|
||||
void MonitorTypeChanged(int value);
|
||||
void TrackChanged();
|
||||
void UseStartOffsetChanged(int);
|
||||
void StartOffsetChanged(const Duration &);
|
||||
void UseDurationChanged(int);
|
||||
void PlaybackDurationChanged(const Duration &);
|
||||
void WaitChanged(int value);
|
||||
|
||||
signals:
|
||||
void HeaderInfoChanged(const QString &);
|
||||
|
||||
private:
|
||||
FileSelection *_filePath;
|
||||
VariableDoubleSpinBox *_volumeDB;
|
||||
QComboBox *_monitorTypes;
|
||||
QWidget *_tracksContainer;
|
||||
std::array<QCheckBox *, 6> _tracks;
|
||||
QCheckBox *_useStartOffset;
|
||||
DurationSelection *_startOffset;
|
||||
QCheckBox *_useDuration;
|
||||
DurationSelection *_playbackDuration;
|
||||
QCheckBox *_waitForCompletion;
|
||||
|
||||
std::shared_ptr<MacroActionPlayAudio> _entryData;
|
||||
bool _loading = true;
|
||||
};
|
||||
|
||||
} // namespace advss
|
||||
Loading…
Reference in New Issue
Block a user