Skip to content

Commit b3cbdf9

Browse files
committed
UI: Add to Settings > Audio ASIO monitoring
This adds a QToolButton to the Audio Settings on Windows. This triggers in turn a dedicated settings for the ASIO monitoring output. If no ASIO driver is detected in the system, the panel displays an explanatory message. The panel allows to: - select an ASIO monitoring device; - for each output channel of the ASIO device, one can select any channel from any of the 6 tracks or from the monitoring mix. Signed-off-by: pkv <[email protected]>
1 parent dbb4802 commit b3cbdf9

File tree

9 files changed

+131
-21
lines changed

9 files changed

+131
-21
lines changed

frontend/data/locale/en-US.ini

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1312,6 +1312,8 @@ Basic.Settings.Advanced.Video.HdrNominalPeakLevel="HDR Nominal Peak Level"
13121312
Basic.Settings.Advanced.Audio.MonitoringDevice="Monitoring Device"
13131313
Basic.Settings.Advanced.Audio.MonitoringDevice.Default="Default"
13141314
Basic.Settings.Advanced.Audio.DisableAudioDucking="Disable Windows audio ducking"
1315+
Basic.Settings.Advanced.Audio.AsioMonitoringDevice="ASIO Monitoring Device"
1316+
Basic.Settings.Audio.AsioMonitoring="Setup Panel"
13151317
Basic.Settings.Advanced.StreamDelay="Stream Delay"
13161318
Basic.Settings.Advanced.StreamDelay.Duration="Duration"
13171319
Basic.Settings.Advanced.StreamDelay.Preserve="Preserve cutoff point (increase delay) when reconnecting"

frontend/data/themes/Yami.obt

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1406,6 +1406,12 @@ QPushButton::menu-indicator {
14061406
width: 25px;
14071407
}
14081408

1409+
QWidget QFormLayout > QToolButton#asioMonitoring {
1410+
min-width: 0px;
1411+
max-width: 16777215px;
1412+
text-align: center;
1413+
}
1414+
14091415
QToolButton {
14101416
border: 1px solid var(--button_border);
14111417
}

frontend/forms/OBSBasicSettings.ui

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6327,6 +6327,29 @@
63276327
</property>
63286328
</widget>
63296329
</item>
6330+
<item row="3" column="0">
6331+
<widget class="QLabel" name="asioDeviceLabel">
6332+
<property name="text">
6333+
<string>Basic.Settings.Advanced.Audio.AsioMonitoringDevice</string>
6334+
</property>
6335+
<property name="buddy">
6336+
<cstring>asioMonitoring</cstring>
6337+
</property>
6338+
</widget>
6339+
</item>
6340+
<item row="3" column="1">
6341+
<widget class="QToolButton" name="asioMonitoring">
6342+
<property name="text">
6343+
<string>Basic.Settings.Audio.AsioMonitoring</string>
6344+
</property>
6345+
<property name="objectName">
6346+
<string>asioMonitoring</string>
6347+
</property>
6348+
<property name="sizePolicy">
6349+
<sizepolicy hsizetype="Minimum" vsizetype="Fixed"/>
6350+
</property>
6351+
</widget>
6352+
</item>
63306353
</layout>
63316354
</widget>
63326355
</item>
@@ -8955,6 +8978,7 @@
89558978
<tabstop>meterDecayRate</tabstop>
89568979
<tabstop>peakMeterType</tabstop>
89578980
<tabstop>monitoringDevice</tabstop>
8981+
<tabstop>asioMonitoring</tabstop>
89588982
<tabstop>disableAudioDucking</tabstop>
89598983
<tabstop>lowLatencyBuffering</tabstop>
89608984
<tabstop>baseResolution</tabstop>

frontend/plugins/asio-output-ui/ASIOSettingsDialog.cpp

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -38,22 +38,30 @@ ASIOSettingsDialog::ASIOSettingsDialog(QWidget *parent, obs_output_t *output, OB
3838
propertiesView = nullptr;
3939
}
4040

41-
void ASIOSettingsDialog::ShowHideDialog()
41+
void ASIOSettingsDialog::ShowHideDialog(bool enabled)
4242
{
43-
SetupPropertiesView();
43+
SetupPropertiesView(enabled);
4444
setVisible(!isVisible());
4545
}
4646

47-
void ASIOSettingsDialog::SetupPropertiesView()
47+
void ASIOSettingsDialog::SetupPropertiesView(bool enabled)
4848
{
4949
if (propertiesView)
5050
delete propertiesView;
5151

5252
propertiesView = new OBSPropertiesView(settings_, "asio_output",
5353
(PropertiesReloadCallback)obs_get_output_properties, 170);
5454

55-
ui->propertiesLayout->addWidget(propertiesView);
56-
currentDeviceName = g_currentDeviceName;
55+
if (enabled) {
56+
ui->propertiesLayout->addWidget(propertiesView);
57+
currentDeviceName = g_currentDeviceName;
58+
} else {
59+
QLabel *noAsioLabel = new QLabel(obs_module_text("AsioOutput.Disabled"), this);
60+
noAsioLabel->setWordWrap(true);
61+
noAsioLabel->setAlignment(Qt::AlignCenter);
62+
ui->propertiesLayout->addWidget(noAsioLabel);
63+
adjustSize();
64+
}
5765

5866
connect(propertiesView, &OBSPropertiesView::Changed, this, &ASIOSettingsDialog::PropertiesChanged);
5967
}

frontend/plugins/asio-output-ui/ASIOSettingsDialog.h

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
#include <QDialog>
3232
#include <QAction>
3333
#include <QMainWindow>
34+
#include <QLabel>
3435

3536
#include "./forms/ui_output.h"
3637

@@ -42,8 +43,8 @@ class ASIOSettingsDialog : public QDialog {
4243
public:
4344
explicit ASIOSettingsDialog(QWidget *parent = 0, obs_output_t *output = nullptr, OBSData settings = nullptr);
4445
std::unique_ptr<Ui_Output> ui;
45-
void ShowHideDialog();
46-
void SetupPropertiesView();
46+
void ShowHideDialog(bool enabled);
47+
void SetupPropertiesView(bool enabled);
4748
void SaveSettings();
4849
OBSData settings_;
4950
obs_output_t *output_;

frontend/plugins/asio-output-ui/asio-ui-main.cpp

Lines changed: 53 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@
2020
#include <obs-frontend-api.h>
2121
#include <QMainWindow>
2222
#include <QAction>
23+
#include <QScreen>
24+
2325
#include <util/util.hpp>
2426
#include <util/platform.h>
2527
#include "ASIOSettingsDialog.h"
@@ -86,21 +88,49 @@ void output_start()
8688
}
8789
}
8890

91+
void callback()
92+
{
93+
QMainWindow *mainWindow = (QMainWindow *)obs_frontend_get_main_window();
94+
QWidget *obsSettingsDialog = nullptr;
95+
const auto topLevels = QApplication::topLevelWidgets();
96+
for (QWidget *widget : topLevels) {
97+
if (widget->isVisible() && QString(widget->metaObject()->className()).contains("OBSBasicSettings")) {
98+
obsSettingsDialog = widget;
99+
break;
100+
}
101+
}
102+
if (!settingsDialog_) {
103+
if (!obsSettingsDialog)
104+
settingsDialog_ = new ASIOSettingsDialog(mainWindow, context.output, context.settings);
105+
else
106+
settingsDialog_ = new ASIOSettingsDialog(obsSettingsDialog, context.output, context.settings);
107+
settingsDialog_->setAttribute(Qt::WA_DeleteOnClose);
108+
QObject::connect(settingsDialog_, &QObject::destroyed, []() { settingsDialog_ = nullptr; });
109+
}
110+
111+
settingsDialog_->ShowHideDialog(context.enabled);
112+
if (obsSettingsDialog) {
113+
QRect settingsRect = obsSettingsDialog->geometry();
114+
QRect asioRect = settingsDialog_->geometry();
115+
QPoint newPos(settingsRect.right() + 100, settingsRect.top());
116+
QScreen *screen = obsSettingsDialog->screen();
117+
QRect desktopRect = screen->availableGeometry();
118+
if (newPos.x() + asioRect.width() > desktopRect.right())
119+
newPos.setX(desktopRect.right() - asioRect.width());
120+
settingsDialog_->move(newPos);
121+
}
122+
}
123+
89124
void addOutputUI(void)
90125
{
91126
QAction *action = (QAction *)obs_frontend_add_tools_menu_qaction(obs_module_text("AsioOutput.Menu"));
92-
93-
QMainWindow *mainWindow = (QMainWindow *)obs_frontend_get_main_window();
127+
action->setObjectName("asioOutputSetupAction");
94128

95129
obs_frontend_push_ui_translation(obs_module_get_string);
96-
settingsDialog_ = new ASIOSettingsDialog(mainWindow, context.output, context.settings);
97130
obs_frontend_pop_ui_translation();
98-
99-
auto cb = []() {
100-
settingsDialog_->ShowHideDialog();
101-
};
102-
103-
action->connect(action, &QAction::triggered, cb);
131+
// the UI is added through the callback, which is triggered in OBS Audio Settings
132+
action->connect(action, &QAction::triggered, callback);
133+
action->setVisible(false);
104134
}
105135

106136
static void OBSEvent(enum obs_frontend_event event, void *)
@@ -131,10 +161,15 @@ void obs_module_unload(void)
131161
if (output_running)
132162
output_stop();
133163

134-
obs_output_release(context.output);
135-
context.output = nullptr;
136-
obs_data_release(context.settings);
137-
context.settings = nullptr;
164+
if (context.output) {
165+
obs_output_release(context.output);
166+
context.output = nullptr;
167+
}
168+
169+
if (context.settings) {
170+
obs_data_release(context.settings);
171+
context.settings = nullptr;
172+
}
138173
obs_frontend_remove_event_callback(OBSEvent, nullptr);
139174
}
140175

@@ -144,8 +179,11 @@ void obs_module_post_load(void)
144179
return;
145180

146181
context.settings = load_settings();
182+
147183
obs_output_t *const output = obs_output_create("asio_output", "asio_output", context.settings, NULL);
184+
148185
if (output != nullptr) {
186+
context.enabled = true;
149187
context.output = output;
150188

151189
if (!context.settings) {
@@ -156,5 +194,7 @@ void obs_module_post_load(void)
156194
obs_frontend_add_event_callback(OBSEvent, nullptr);
157195
} else {
158196
blog(LOG_INFO, "Failed to create ASIO output");
197+
// we add the UI even if there is no output to display a text saying ASIO is disabled
198+
addOutputUI();
159199
}
160200
}
Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1,2 @@
1-
AsioOutput.Menu="ASIO Output"
1+
AsioOutput.Menu="ASIO Output"
2+
AsioOutput.Disabled="No ASIO audio driver was detected in your system. ASIO monitoring is disabled."

frontend/settings/OBSBasicSettings.cpp

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -719,6 +719,14 @@ OBSBasicSettings::OBSBasicSettings(QWidget *parent)
719719
if (obs_audio_monitoring_available())
720720
FillAudioMonitoringDevices();
721721

722+
#ifdef _WIN32
723+
connect(ui->asioMonitoring, &QPushButton::clicked, this, &OBSBasicSettings::AsioMonitoringShow);
724+
ui->asioMonitoring->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Fixed);
725+
ui->formLayout_56->setAlignment(ui->asioMonitoring, Qt::AlignLeft);
726+
#else
727+
ui->asioMonitoring->hide();
728+
ui->asioDeviceLabel->hide();
729+
#endif
722730
connect(ui->channelSetup, &QComboBox::currentIndexChanged, this, &OBSBasicSettings::SurroundWarning);
723731
connect(ui->channelSetup, &QComboBox::currentIndexChanged, this, &OBSBasicSettings::SpeakerLayoutChanged);
724732
connect(ui->lowLatencyBuffering, &QCheckBox::clicked, this, &OBSBasicSettings::LowLatencyBufferingChanged);
@@ -5288,6 +5296,23 @@ void OBSBasicSettings::SimpleRecordingEncoderChanged()
52885296
ui->simpleOutInfoLayout->addWidget(simpleOutRecWarning);
52895297
}
52905298

5299+
#ifdef _WIN32
5300+
void OBSBasicSettings::AsioMonitoringShow()
5301+
{
5302+
QList<QAction *> actions = main->ui->menuTools->actions();
5303+
QAction *asioAction = nullptr;
5304+
for (QAction *action : actions) {
5305+
if (action->objectName() == "asioOutputSetupAction") {
5306+
asioAction = action;
5307+
break;
5308+
}
5309+
}
5310+
if (asioAction) {
5311+
asioAction->trigger();
5312+
}
5313+
}
5314+
#endif
5315+
52915316
void OBSBasicSettings::SurroundWarning(int idx)
52925317
{
52935318
if (idx == lastChannelSetupIdx || idx == -1)

frontend/settings/OBSBasicSettings.hpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -396,6 +396,9 @@ private slots:
396396
void AudioChanged();
397397
void AudioChangedRestart();
398398
void ReloadAudioSources();
399+
#ifdef _WIN32
400+
void AsioMonitoringShow();
401+
#endif
399402
void SurroundWarning(int idx);
400403
void SpeakerLayoutChanged(int idx);
401404
void LowLatencyBufferingChanged(bool checked);

0 commit comments

Comments
 (0)