Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
102 changes: 50 additions & 52 deletions frontend/components/VolumeControl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -95,10 +95,11 @@ VolumeControl::VolumeControl(obs_source_t *source, QWidget *parent, bool vertica

volumeMeter = new VolumeMeter(this, source);

bool muted = obs_source_muted(source);
obsMuted = obs_source_muted(source);
bool unassigned = isSourceUnassigned(source);
obsMonitoringType = obs_source_get_monitoring_type(source);

volumeMeter->setMuted(muted || unassigned);
volumeMeter->setMuted(obsMuted || unassigned);

setLayoutVertical(vertical);
setName(sourceName);
Expand All @@ -107,10 +108,8 @@ VolumeControl::VolumeControl(obs_source_t *source, QWidget *parent, bool vertica

obsSignals.reserve(9);
obsSignals.emplace_back(obs_source_get_signal_handler(source), "mute", obsVolumeMuted, this);
obsSignals.emplace_back(obs_source_get_signal_handler(source), "audio_mixers", obsMixersOrMonitoringChanged,
this);
obsSignals.emplace_back(obs_source_get_signal_handler(source), "audio_monitoring", obsMixersOrMonitoringChanged,
this);
obsSignals.emplace_back(obs_source_get_signal_handler(source), "audio_mixers", obsMixersChanged, this);
obsSignals.emplace_back(obs_source_get_signal_handler(source), "audio_monitoring", obsMonitoringChanged, this);
obsSignals.emplace_back(obs_source_get_signal_handler(source), "activate", VolumeControl::obsSourceActivated,
this);
obsSignals.emplace_back(obs_source_get_signal_handler(source), "deactivate",
Expand Down Expand Up @@ -151,7 +150,7 @@ VolumeControl::VolumeControl(obs_source_t *source, QWidget *parent, bool vertica
// Call volume changed once to init the slider position and label
changeVolume();

updateMixerState();
processMixerState();
}

VolumeControl::~VolumeControl()
Expand Down Expand Up @@ -202,34 +201,43 @@ void VolumeControl::obsVolumeChanged(void *data, float)
QMetaObject::invokeMethod(volControl, "changeVolume", Qt::QueuedConnection);
}

void VolumeControl::obsVolumeMuted(void *data, calldata_t *)
void VolumeControl::obsVolumeMuted(void *data, calldata_t *params)
{
VolumeControl *volControl = static_cast<VolumeControl *>(data);
bool muted = calldata_bool(params, "muted");

QMetaObject::invokeMethod(volControl, "updateMixerState", Qt::QueuedConnection);
QMetaObject::invokeMethod(volControl, "onMuteChanged", Qt::QueuedConnection, Q_ARG(bool, muted));
}

void VolumeControl::obsMixersOrMonitoringChanged(void *data, calldata_t *)
void VolumeControl::obsMixersChanged(void *data, calldata_t *)
{
VolumeControl *volControl = static_cast<VolumeControl *>(data);
QMetaObject::invokeMethod(volControl, "updateMixerState", Qt::QueuedConnection);
QMetaObject::invokeMethod(volControl, "processMixerState", Qt::QueuedConnection);
}

void VolumeControl::obsMonitoringChanged(void *data, calldata_t *params)
{
VolumeControl *volControl = static_cast<VolumeControl *>(data);
auto type = static_cast<int>(calldata_int(params, "type"));

QMetaObject::invokeMethod(volControl, "onMonitoringChanged", Qt::QueuedConnection, Q_ARG(int, type));
}

void VolumeControl::obsSourceActivated(void *data, calldata_t *)
{
QMetaObject::invokeMethod(static_cast<VolumeControl *>(data), "sourceActiveChanged", Qt::QueuedConnection,
QMetaObject::invokeMethod(static_cast<VolumeControl *>(data), "onSourceActiveChanged", Qt::QueuedConnection,
Q_ARG(bool, true));
}

void VolumeControl::obsSourceDeactivated(void *data, calldata_t *)
{
QMetaObject::invokeMethod(static_cast<VolumeControl *>(data), "sourceActiveChanged", Qt::QueuedConnection,
QMetaObject::invokeMethod(static_cast<VolumeControl *>(data), "onSourceActiveChanged", Qt::QueuedConnection,
Q_ARG(bool, false));
}

void VolumeControl::obsSourceDestroy(void *data, calldata_t *)
{
QMetaObject::invokeMethod(static_cast<VolumeControl *>(data), "handleSourceDestroyed", Qt::QueuedConnection);
QMetaObject::invokeMethod(static_cast<VolumeControl *>(data), "onSourceDestroyed", Qt::QueuedConnection);
}

void VolumeControl::setLayoutVertical(bool vertical)
Expand Down Expand Up @@ -594,6 +602,18 @@ void VolumeControl::setLocked(bool locked)
emit main->mixerStatusChanged(uuid);
}

void VolumeControl::onMuteChanged(bool muted)
{
obsMuted = muted;
processMixerState();
}

void VolumeControl::onMonitoringChanged(int type)
{
obsMonitoringType = static_cast<obs_monitoring_type>(type);
processMixerState();
}

void VolumeControl::updateCategoryLabel()
{
QString labelText = QTStr("Basic.AudioMixer.Category.Active");
Expand Down Expand Up @@ -722,7 +742,7 @@ void VolumeControl::setMonitoring(obs_monitoring_type type)
std::bind(undo_redo, std::placeholders::_1, type), uuid, uuid);
}

void VolumeControl::sourceActiveChanged(bool active)
void VolumeControl::onSourceActiveChanged(bool active)
{
setUseDisabledColors(!active);
mixerStatus().set(VolumeControl::MixerStatus::Active, active);
Expand All @@ -731,17 +751,15 @@ void VolumeControl::sourceActiveChanged(bool active)
emit main->mixerStatusChanged(uuid);
}

void VolumeControl::updateMixerState()
void VolumeControl::processMixerState()
{
OBSSource source = OBSGetStrongRef(weakSource());
if (!source) {
deleteLater();
return;
}

bool muted = obs_source_muted(source);
bool unassigned = isSourceUnassigned(source);
obs_monitoring_type monitoringType = obs_source_get_monitoring_type(source);

bool isActive = obs_source_active(source) && obs_source_audio_active(source);

Expand All @@ -751,9 +769,9 @@ void VolumeControl::updateMixerState()
QSignalBlocker blockMute(muteButton);
QSignalBlocker blockMonitor(monitorButton);

bool showAsMuted = muted || monitoringType == OBS_MONITORING_TYPE_MONITOR_ONLY;
bool showAsMonitored = !muted && monitoringType != OBS_MONITORING_TYPE_NONE;
bool showAsUnassigned = !muted && unassigned;
bool showAsMuted = obsMuted || obsMonitoringType == OBS_MONITORING_TYPE_MONITOR_ONLY;
bool showAsMonitored = obsMonitoringType != OBS_MONITORING_TYPE_NONE;
bool showAsUnassigned = !obsMuted && unassigned;

volumeMeter->setMuted((showAsMuted || showAsUnassigned) && !showAsMonitored);
setUseDisabledColors(showAsMuted || !isActive);
Expand Down Expand Up @@ -797,47 +815,27 @@ void VolumeControl::updateMixerState()

void VolumeControl::handleMuteButton(bool mute)
{
OBSSource source = OBSGetStrongRef(weakSource());
if (!source) {
return;
}

// The Mute and Monitor buttons in the volume mixer work as a pseudo quad-state toggle.
// Both buttons must be in their "off" state in order to actually process it as a mute.
// Otherwise, clicking "Mute" with monitoring enabled will toggle the monitoring type.
obs_monitoring_type monitoringType = obs_source_get_monitoring_type(source);
setMuted(mute);

if (mute && monitoringType == OBS_MONITORING_TYPE_MONITOR_AND_OUTPUT) {
setMonitoring(OBS_MONITORING_TYPE_MONITOR_ONLY);
} else if (!mute && monitoringType == OBS_MONITORING_TYPE_MONITOR_ONLY) {
setMonitoring(OBS_MONITORING_TYPE_MONITOR_AND_OUTPUT);
} else {
setMuted(mute);
if (obsMonitoringType != OBS_MONITORING_TYPE_NONE) {
if (mute) {
setMonitoring(OBS_MONITORING_TYPE_MONITOR_ONLY);
} else {
setMonitoring(OBS_MONITORING_TYPE_MONITOR_AND_OUTPUT);
}
}
}

void VolumeControl::handleMonitorButton(bool enableMonitoring)
{
OBSSource source = OBSGetStrongRef(weakSource());
if (!source) {
if (!enableMonitoring) {
setMonitoring(OBS_MONITORING_TYPE_NONE);
return;
}

// The Mute and Monitor buttons in the volume mixer work as a pseudo quad-state toggle.
// The source is only ever actually "Muted" if Monitoring is set to None.
obs_monitoring_type monitoringType = obs_source_get_monitoring_type(source);

bool muted = obs_source_muted(source);

if (!enableMonitoring) {
setMonitoring(OBS_MONITORING_TYPE_NONE);
if (monitoringType == OBS_MONITORING_TYPE_MONITOR_ONLY) {
setMuted(true);
}
} else if (enableMonitoring && muted) {
if (obsMuted) {
setMonitoring(OBS_MONITORING_TYPE_MONITOR_ONLY);
setMuted(false);
} else if (enableMonitoring && !muted) {
} else {
setMonitoring(OBS_MONITORING_TYPE_MONITOR_AND_OUTPUT);
}
}
Expand Down
13 changes: 9 additions & 4 deletions frontend/components/VolumeControl.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,8 @@ class VolumeControl : public QFrame {
OBSWeakSource weakSource_;
const char *uuid;
std::vector<OBSSignal> obsSignals;
obs_monitoring_type obsMonitoringType;
bool obsMuted;

QBoxLayout *mainLayout;
QLabel *categoryLabel;
Expand Down Expand Up @@ -98,7 +100,8 @@ class VolumeControl : public QFrame {

static void obsVolumeChanged(void *param, float db);
static void obsVolumeMuted(void *data, calldata_t *calldata);
static void obsMixersOrMonitoringChanged(void *data, calldata_t *);
static void obsMixersChanged(void *data, calldata_t *);
static void obsMonitoringChanged(void *data, calldata_t *);
static void obsSourceActivated(void *data, calldata_t *params);
static void obsSourceDeactivated(void *data, calldata_t *params);
static void obsSourceDestroy(void *data, calldata_t *params);
Expand All @@ -112,10 +115,9 @@ class VolumeControl : public QFrame {
void setMonitoring(obs_monitoring_type type);

public slots:
void sourceActiveChanged(bool active);
void setUseDisabledColors(bool greyscale);
void setLocked(bool locked);
void updateMixerState();
void processMixerState();

private slots:
void renameSource();
Expand All @@ -127,7 +129,10 @@ private slots:
void updateText();
void setName(QString name);

void handleSourceDestroyed() { deleteLater(); }
void onSourceActiveChanged(bool active);
void onMuteChanged(bool muted);
void onMonitoringChanged(int type);
void onSourceDestroyed() { deleteLater(); }

signals:
void unhideAll();
Expand Down
2 changes: 1 addition & 1 deletion frontend/components/VolumeMeter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -364,7 +364,7 @@ VolumeMeter::~VolumeMeter()
void VolumeMeter::obsSourceDestroyed(void *data, calldata_t *)
{
VolumeMeter *self = static_cast<VolumeMeter *>(data);
QMetaObject::invokeMethod(self, "handleSourceDestroyed", Qt::QueuedConnection);
QMetaObject::invokeMethod(self, "onSourceDestroyed", Qt::QueuedConnection);
}

void VolumeMeter::setLevels(const float magnitude[MAX_AUDIO_CHANNELS], const float peak[MAX_AUDIO_CHANNELS],
Expand Down
2 changes: 1 addition & 1 deletion frontend/widgets/AudioMixer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -371,7 +371,7 @@ void AudioMixer::updateControlVisibility(QString uuid)
bool show = getMixerVisibilityForControl(control);

if (show) {
control->updateMixerState();
control->processMixerState();
control->show();
} else {
control->hide();
Expand Down
19 changes: 8 additions & 11 deletions libobs/audio-monitoring/osx/coreaudio-output.c
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,8 @@ static void on_audio_pause(void *data, calldata_t *calldata)
pthread_mutex_unlock(&monitor->mutex);
}

static void on_audio_playback(void *param, obs_source_t *source, const struct audio_data *audio_data, bool muted)
static void on_audio_playback(void *param, obs_source_t *source, const struct audio_data *audio_data,
bool muted __unused)
{
struct audio_monitor *monitor = param;
float vol = source->user_volume;
Expand All @@ -88,17 +89,13 @@ static void on_audio_playback(void *param, obs_source_t *source, const struct au

bytes = sizeof(float) * monitor->channels * resample_frames;

if (muted) {
memset(resample_data[0], 0, bytes);
} else {
/* apply volume */
if (!close_float(vol, 1.0f, EPSILON)) {
register float *cur = (float *)resample_data[0];
register float *end = cur + resample_frames * monitor->channels;
/* apply volume */
if (!close_float(vol, 1.0f, EPSILON)) {
register float *cur = (float *)resample_data[0];
register float *end = cur + resample_frames * monitor->channels;

while (cur < end)
*(cur++) *= vol;
}
while (cur < end)
*(cur++) *= vol;
}

pthread_mutex_lock(&monitor->mutex);
Expand Down
10 changes: 3 additions & 7 deletions libobs/audio-monitoring/pulse/pulseaudio-output.c
Original file line number Diff line number Diff line change
Expand Up @@ -227,7 +227,7 @@ static void do_stream_write(void *param)
pulseaudio_unlock();
}

static void on_audio_playback(void *param, obs_source_t *source, const struct audio_data *audio_data, bool muted)
static void on_audio_playback(void *param, obs_source_t *source, const struct audio_data *audio_data, bool)
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm surprised this works, because that's usually a C++ feature, so maybe a GNU extension?

Copy link
Copy Markdown
Member

@RytoEX RytoEX May 6, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We do have C extensions enabled for Linux because libobs doesn't compile without them there.

if(CMAKE_HOST_SYSTEM_NAME STREQUAL "Windows")
set(CMAKE_C_EXTENSIONS FALSE)
set(CMAKE_CXX_EXTENSIONS FALSE)
list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake/windows")
set(OS_WINDOWS TRUE)
elseif(CMAKE_HOST_SYSTEM_NAME STREQUAL "Darwin")
set(CMAKE_C_EXTENSIONS FALSE)
set(CMAKE_CXX_EXTENSIONS FALSE)
list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake/macos")
set(OS_MACOS TRUE)
elseif(CMAKE_HOST_SYSTEM_NAME MATCHES "Linux|FreeBSD|OpenBSD")
set(CMAKE_CXX_EXTENSIONS FALSE)
list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake/linux")

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yep we need that for stuff like POSIX threads to work IIRC, was just wondering if that's the reason why that works on Linux, because it won't on macOS and I'm not sure whether MSVC accepts it either.

{
struct audio_monitor *monitor = param;
float vol = source->user_volume;
Expand All @@ -252,12 +252,8 @@ static void on_audio_playback(void *param, obs_source_t *source, const struct au

bytes = monitor->bytes_per_frame * resample_frames;

if (muted) {
memset(resample_data[0], 0, bytes);
} else {
if (!close_float(vol, 1.0f, EPSILON)) {
process_volume(monitor, vol, resample_data, resample_frames);
}
if (!close_float(vol, 1.0f, EPSILON)) {
process_volume(monitor, vol, resample_data, resample_frames);
}

deque_push_back(&monitor->new_data, resample_data[0], bytes);
Expand Down
20 changes: 10 additions & 10 deletions libobs/audio-monitoring/win32/wasapi-output.c
Original file line number Diff line number Diff line change
Expand Up @@ -278,6 +278,8 @@ static void audio_monitor_free_for_reconnect(struct audio_monitor *monitor)

static void on_audio_playback(void *param, obs_source_t *source, const struct audio_data *audio_data, bool muted)
{
UNUSED_PARAMETER(muted);

struct audio_monitor *monitor = param;
uint8_t *resample_data[MAX_AV_PLANES];
float vol = source->user_volume;
Expand Down Expand Up @@ -325,19 +327,17 @@ static void on_audio_playback(void *param, obs_source_t *source, const struct au
goto free_for_reconnect;
}

if (!muted) {
/* apply volume */
if (!close_float(vol, 1.0f, EPSILON)) {
register float *cur = (float *)resample_data[0];
register float *end = cur + resample_frames * monitor->channels;
/* apply volume */
if (!close_float(vol, 1.0f, EPSILON)) {
register float *cur = (float *)resample_data[0];
register float *end = cur + resample_frames * monitor->channels;

while (cur < end)
*(cur++) *= vol;
}
memcpy(output, resample_data[0], resample_frames * monitor->channels * sizeof(float));
while (cur < end)
*(cur++) *= vol;
}
memcpy(output, resample_data[0], resample_frames * monitor->channels * sizeof(float));

hr = render->lpVtbl->ReleaseBuffer(render, resample_frames, muted ? AUDCLNT_BUFFERFLAGS_SILENT : 0);
hr = render->lpVtbl->ReleaseBuffer(render, resample_frames, 0);
if (FAILED(hr)) {
goto free_for_reconnect;
}
Expand Down
Loading