Skip to content

Commit cfa6be3

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 b5b6cac commit cfa6be3

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
@@ -1309,6 +1309,8 @@ Basic.Settings.Advanced.Video.HdrNominalPeakLevel="HDR Nominal Peak Level"
13091309
Basic.Settings.Advanced.Audio.MonitoringDevice="Monitoring Device"
13101310
Basic.Settings.Advanced.Audio.MonitoringDevice.Default="Default"
13111311
Basic.Settings.Advanced.Audio.DisableAudioDucking="Disable Windows audio ducking"
1312+
Basic.Settings.Advanced.Audio.AsioMonitoringDevice="ASIO Monitoring Device"
1313+
Basic.Settings.Audio.AsioMonitoring="Setup Panel"
13121314
Basic.Settings.Advanced.StreamDelay="Stream Delay"
13131315
Basic.Settings.Advanced.StreamDelay.Duration="Duration"
13141316
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
@@ -6233,6 +6233,29 @@
62336233
</property>
62346234
</widget>
62356235
</item>
6236+
<item row="3" column="0">
6237+
<widget class="QLabel" name="asioDeviceLabel">
6238+
<property name="text">
6239+
<string>Basic.Settings.Advanced.Audio.AsioMonitoringDevice</string>
6240+
</property>
6241+
<property name="buddy">
6242+
<cstring>asioMonitoring</cstring>
6243+
</property>
6244+
</widget>
6245+
</item>
6246+
<item row="3" column="1">
6247+
<widget class="QToolButton" name="asioMonitoring">
6248+
<property name="text">
6249+
<string>Basic.Settings.Audio.AsioMonitoring</string>
6250+
</property>
6251+
<property name="objectName">
6252+
<string>asioMonitoring</string>
6253+
</property>
6254+
<property name="sizePolicy">
6255+
<sizepolicy hsizetype="Minimum" vsizetype="Fixed"/>
6256+
</property>
6257+
</widget>
6258+
</item>
62366259
</layout>
62376260
</widget>
62386261
</item>
@@ -8861,6 +8884,7 @@
88618884
<tabstop>meterDecayRate</tabstop>
88628885
<tabstop>peakMeterType</tabstop>
88638886
<tabstop>monitoringDevice</tabstop>
8887+
<tabstop>asioMonitoring</tabstop>
88648888
<tabstop>disableAudioDucking</tabstop>
88658889
<tabstop>lowLatencyBuffering</tabstop>
88668890
<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
@@ -718,6 +718,14 @@ OBSBasicSettings::OBSBasicSettings(QWidget *parent)
718718
if (obs_audio_monitoring_available())
719719
FillAudioMonitoringDevices();
720720

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

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