diff --git a/UI/adv-audio-control.cpp b/UI/adv-audio-control.cpp index 2c602b6a13eb5170324c947c1723eda781040f47..e84e7c227750ddf8621ebd6239735be46f9f8c45 100644 --- a/UI/adv-audio-control.cpp +++ b/UI/adv-audio-control.cpp @@ -8,6 +8,7 @@ #include "qt-wrappers.hpp" #include "obs-app.hpp" #include "adv-audio-control.hpp" +#include "window-basic-main.hpp" #ifndef NSEC_PER_MSEC #define NSEC_PER_MSEC 1000000 @@ -25,13 +26,13 @@ OBSAdvAudioCtrl::OBSAdvAudioCtrl(QGridLayout *, obs_source_t *source_) forceMonoContainer = new QWidget(); mixerContainer = new QWidget(); - panningContainer = new QWidget(); + balanceContainer = new QWidget(); labelL = new QLabel(); labelR = new QLabel(); nameLabel = new QLabel(); volume = new QSpinBox(); forceMono = new QCheckBox(); - panning = new QSlider(Qt::Horizontal); + balance = new QSlider(Qt::Horizontal); #if defined(_WIN32) || defined(__APPLE__) || HAVE_PULSEAUDIO monitoringType = new QComboBox(); #endif @@ -60,8 +61,8 @@ OBSAdvAudioCtrl::OBSAdvAudioCtrl(QGridLayout *, obs_source_t *source_) mixerContainer->setLayout(hlayout); hlayout = new QHBoxLayout(); hlayout->setContentsMargins(0, 0, 0, 0); - panningContainer->setLayout(hlayout); - panningContainer->setMinimumWidth(100); + balanceContainer->setLayout(hlayout); + balanceContainer->setMinimumWidth(100); labelL->setText("L"); @@ -81,11 +82,23 @@ OBSAdvAudioCtrl::OBSAdvAudioCtrl(QGridLayout *, obs_source_t *source_) forceMonoContainer->layout()->setAlignment(forceMono, Qt::AlignHCenter | Qt::AlignVCenter); - panning->setMinimum(0); - panning->setMaximum(100); - panning->setTickPosition(QSlider::TicksAbove); - panning->setEnabled(false); - panning->setValue(50); /* XXX */ + balance->setMinimum(0); + balance->setMaximum(100); + balance->setTickPosition(QSlider::TicksAbove); + balance->setTickInterval(50); + + OBSBasic *main = reinterpret_cast(App()->GetMainWindow()); + + const char *speakers = config_get_string(main->Config(), "Audio", + "ChannelSetup"); + + if (strcmp(speakers, "Mono") == 0) + balance->setEnabled(false); + else + balance->setEnabled(true); + + float bal = obs_source_get_balance_value(source) * 100.0f; + balance->setValue((int)bal); int64_t cur_sync = obs_source_get_sync_offset(source); syncOffset->setMinimum(-20000); @@ -118,10 +131,14 @@ OBSAdvAudioCtrl::OBSAdvAudioCtrl(QGridLayout *, obs_source_t *source_) mixer6->setText("6"); mixer6->setChecked(mixers & (1<<5)); - panningContainer->layout()->addWidget(labelL); - panningContainer->layout()->addWidget(panning); - panningContainer->layout()->addWidget(labelR); - panningContainer->setMaximumWidth(170); + speaker_layout sl = obs_source_get_speaker_layout(source); + + if (sl == SPEAKERS_STEREO) { + balanceContainer->layout()->addWidget(labelL); + balanceContainer->layout()->addWidget(balance); + balanceContainer->layout()->addWidget(labelR); + balanceContainer->setMaximumWidth(170); + } mixerContainer->layout()->addWidget(mixer1); mixerContainer->layout()->addWidget(mixer2); @@ -134,8 +151,8 @@ OBSAdvAudioCtrl::OBSAdvAudioCtrl(QGridLayout *, obs_source_t *source_) this, SLOT(volumeChanged(int))); QWidget::connect(forceMono, SIGNAL(clicked(bool)), this, SLOT(downmixMonoChanged(bool))); - QWidget::connect(panning, SIGNAL(valueChanged(int)), - this, SLOT(panningChanged(int))); + QWidget::connect(balance, SIGNAL(valueChanged(int)), + this, SLOT(balanceChanged(int))); QWidget::connect(syncOffset, SIGNAL(valueChanged(int)), this, SLOT(syncOffsetChanged(int))); #if defined(_WIN32) || defined(__APPLE__) || HAVE_PULSEAUDIO @@ -163,7 +180,7 @@ OBSAdvAudioCtrl::~OBSAdvAudioCtrl() nameLabel->deleteLater(); volume->deleteLater(); forceMonoContainer->deleteLater(); - panningContainer->deleteLater(); + balanceContainer->deleteLater(); syncOffset->deleteLater(); #if defined(_WIN32) || defined(__APPLE__) || HAVE_PULSEAUDIO monitoringType->deleteLater(); @@ -179,7 +196,7 @@ void OBSAdvAudioCtrl::ShowAudioControl(QGridLayout *layout) layout->addWidget(nameLabel, lastRow, idx++); layout->addWidget(volume, lastRow, idx++); layout->addWidget(forceMonoContainer, lastRow, idx++); - layout->addWidget(panningContainer, lastRow, idx++); + layout->addWidget(balanceContainer, lastRow, idx++); layout->addWidget(syncOffset, lastRow, idx++); #if defined(_WIN32) || defined(__APPLE__) || HAVE_PULSEAUDIO layout->addWidget(monitoringType, lastRow, idx++); @@ -283,10 +300,18 @@ void OBSAdvAudioCtrl::downmixMonoChanged(bool checked) } } -void OBSAdvAudioCtrl::panningChanged(int val) +void OBSAdvAudioCtrl::balanceChanged(int val) { - /* TODO */ - UNUSED_PARAMETER(val); + float bal = (float)val / 100.0f; + + if (abs(50 - val) < 10) { + balance->blockSignals(true); + balance->setValue(50); + bal = 0.5f; + balance->blockSignals(false); + } + + obs_source_set_balance_value(source, bal); } void OBSAdvAudioCtrl::syncOffsetChanged(int milliseconds) diff --git a/UI/adv-audio-control.hpp b/UI/adv-audio-control.hpp index 1352a4b8f3dde7d4633305fcc00bdc5196f7b66c..a075390b012f8eb592704ae9e514e207e9c5dfc2 100644 --- a/UI/adv-audio-control.hpp +++ b/UI/adv-audio-control.hpp @@ -19,12 +19,12 @@ private: QPointer forceMonoContainer; QPointer mixerContainer; - QPointer panningContainer; + QPointer balanceContainer; QPointer nameLabel; QPointer volume; QPointer forceMono; - QPointer panning; + QPointer balance; QPointer labelL; QPointer labelR; QPointer syncOffset; @@ -61,7 +61,7 @@ public slots: void volumeChanged(int percentage); void downmixMonoChanged(bool checked); - void panningChanged(int val); + void balanceChanged(int val); void syncOffsetChanged(int milliseconds); void monitoringTypeChanged(int index); void mixer1Changed(bool checked); diff --git a/UI/data/locale/en-US.ini b/UI/data/locale/en-US.ini index ca6d6a74095147329dba87debf9da186b9e95057..08145eb8d5505c9f4a3c8e54ca2f02e4f3fa6586 100644 --- a/UI/data/locale/en-US.ini +++ b/UI/data/locale/en-US.ini @@ -768,7 +768,7 @@ Basic.AdvAudio="Advanced Audio Properties" Basic.AdvAudio.Name="Name" Basic.AdvAudio.Volume="Volume (%)" Basic.AdvAudio.Mono="Downmix to Mono" -Basic.AdvAudio.Panning="Panning" +Basic.AdvAudio.Balance="Balance" Basic.AdvAudio.SyncOffset="Sync Offset (ms)" Basic.AdvAudio.Monitoring="Audio Monitoring" Basic.AdvAudio.Monitoring.None="Monitor Off" diff --git a/UI/window-basic-adv-audio.cpp b/UI/window-basic-adv-audio.cpp index e6b47f7beb88ee5b8600d70914482dfcb6741c07..3124019011e56be48e5414762a928aca84de111b 100644 --- a/UI/window-basic-adv-audio.cpp +++ b/UI/window-basic-adv-audio.cpp @@ -37,7 +37,7 @@ OBSBasicAdvAudio::OBSBasicAdvAudio(QWidget *parent) label = new QLabel(QTStr("Basic.AdvAudio.Mono")); label->setAlignment(Qt::AlignHCenter); mainLayout->addWidget(label, 0, idx++); - label = new QLabel(QTStr("Basic.AdvAudio.Panning")); + label = new QLabel(QTStr("Basic.AdvAudio.Balance")); label->setAlignment(Qt::AlignHCenter); mainLayout->addWidget(label, 0, idx++); label = new QLabel(QTStr("Basic.AdvAudio.SyncOffset")); diff --git a/libobs/obs-internal.h b/libobs/obs-internal.h index b317fd9527e35f8b8570c5630404fc502fc83157..0aea022470bd6c674c2c5c33b696dd5d4df1a7c5 100644 --- a/libobs/obs-internal.h +++ b/libobs/obs-internal.h @@ -608,6 +608,7 @@ struct obs_source { float volume; int64_t sync_offset; int64_t last_sync_offset; + float balance; /* async video data */ gs_texture_t *async_texture; diff --git a/libobs/obs-source.c b/libobs/obs-source.c index d77be32497e07707ed45c7284d51724bf77c25b3..695156995b9ea4cd9dadb473fb8922ff625e7385 100644 --- a/libobs/obs-source.c +++ b/libobs/obs-source.c @@ -16,6 +16,7 @@ ******************************************************************************/ #include +#include #include "media-io/format-conversion.h" #include "media-io/video-frame.h" @@ -140,6 +141,7 @@ bool obs_source_init(struct obs_source *source) source->user_volume = 1.0f; source->volume = 1.0f; source->sync_offset = 0; + source->balance = 0.5f; pthread_mutex_init_value(&source->filter_mutex); pthread_mutex_init_value(&source->async_mutex); pthread_mutex_init_value(&source->audio_mutex); @@ -2589,6 +2591,37 @@ static void downmix_to_mono_planar(struct obs_source *source, uint32_t frames) } } +static void process_audio_balancing(struct obs_source *source, uint32_t frames, + float balance, enum obs_balance_type type) +{ + float **data = (float**)source->audio_data.data; + + switch(type) { + case OBS_BALANCE_TYPE_SINE_LAW: + for (uint32_t frame = 0; frame < frames; frame++) { + data[0][frame] = data[0][frame] * + sinf((1.0f - balance) * (M_PI/2.0f)); + data[1][frame] = data[1][frame] * + sinf(balance * (M_PI/2.0f)); + } + break; + case OBS_BALANCE_TYPE_SQUARE_LAW: + for (uint32_t frame = 0; frame < frames; frame++) { + data[0][frame] = data[0][frame] * sqrtf(1.0f - balance); + data[1][frame] = data[1][frame] * sqrtf(balance); + } + break; + case OBS_BALANCE_TYPE_LINEAR: + for (uint32_t frame = 0; frame < frames; frame++) { + data[0][frame] = data[0][frame] * (1.0f - balance); + data[1][frame] = data[1][frame] * balance; + } + break; + default: + break; + } +} + /* resamples/remixes new audio to the designated main audio output format */ static void process_audio(obs_source_t *source, const struct obs_source_audio *audio) @@ -2622,6 +2655,13 @@ static void process_audio(obs_source_t *source, mono_output = audio_output_get_channels(obs->audio.audio) == 1; + if ((!mono_output && source->sample_info.speakers == SPEAKERS_STEREO) || + !(source->balance <= 0.51f && + source->balance >= 0.49f)) { + process_audio_balancing(source, frames, source->balance, + OBS_BALANCE_TYPE_SINE_LAW); + } + if (!mono_output && (source->flags & OBS_SOURCE_FLAG_FORCE_MONO) != 0) downmix_to_mono_planar(source, frames); } @@ -4165,3 +4205,25 @@ EXPORT void obs_enable_source_type(const char *name, bool enable) else info->output_flags |= OBS_SOURCE_CAP_DISABLED; } + +enum speaker_layout obs_source_get_speaker_layout(obs_source_t *source) +{ + if (!obs_source_valid(source, "obs_source_get_audio_channels")) + return SPEAKERS_UNKNOWN; + + return source->sample_info.speakers; +} + +void obs_source_set_balance_value(obs_source_t *source, float balance) +{ + if (!obs_source_valid(source, "obs_source_set_balance_value")) + return; + + source->balance = balance; +} + +float obs_source_get_balance_value(const obs_source_t *source) +{ + return obs_source_valid(source, "obs_source_get_balance_value") ? + source->balance : 0.5f; +} diff --git a/libobs/obs-source.h b/libobs/obs-source.h index 04215e95b10281a3090d5f7634ea9036512691ad..fdda90dc70088d3160afdb10f5fac438e39fb3e7 100644 --- a/libobs/obs-source.h +++ b/libobs/obs-source.h @@ -38,6 +38,11 @@ enum obs_source_type { OBS_SOURCE_TYPE_SCENE, }; +enum obs_balance_type { + OBS_BALANCE_TYPE_SINE_LAW, + OBS_BALANCE_TYPE_SQUARE_LAW, + OBS_BALANCE_TYPE_LINEAR, +}; /** * @name Source output flags diff --git a/libobs/obs.c b/libobs/obs.c index acd51bbb6df242a196ccec7c367bdb76b96db238..0b1d90f623475a079243c6cf86b9829314495808 100644 --- a/libobs/obs.c +++ b/libobs/obs.c @@ -1563,6 +1563,7 @@ static obs_source_t *obs_load_source_type(obs_data_t *source_data) obs_data_t *settings = obs_data_get_obj(source_data, "settings"); obs_data_t *hotkeys = obs_data_get_obj(source_data, "hotkeys"); double volume; + double balance; int64_t sync; uint32_t flags; uint32_t mixers; @@ -1578,6 +1579,10 @@ static obs_source_t *obs_load_source_type(obs_data_t *source_data) volume = obs_data_get_double(source_data, "volume"); obs_source_set_volume(source, (float)volume); + obs_data_set_default_double(source_data, "balance", 0.5); + balance = obs_data_get_double(source_data, "balance"); + obs_source_set_balance_value(source, (float)balance); + sync = obs_data_get_int(source_data, "sync"); obs_source_set_sync_offset(source, sync); @@ -1716,6 +1721,7 @@ obs_data_t *obs_save_source(obs_source_t *source) obs_data_t *hotkey_data = source->context.hotkey_data; obs_data_t *hotkeys; float volume = obs_source_get_volume(source); + float balance = obs_source_get_balance_value(source); uint32_t mixers = obs_source_get_audio_mixers(source); int64_t sync = obs_source_get_sync_offset(source); uint32_t flags = obs_source_get_flags(source); @@ -1748,6 +1754,7 @@ obs_data_t *obs_save_source(obs_source_t *source) obs_data_set_int (source_data, "sync", sync); obs_data_set_int (source_data, "flags", flags); obs_data_set_double(source_data, "volume", volume); + obs_data_set_double(source_data, "balance", balance); obs_data_set_bool (source_data, "enabled", enabled); obs_data_set_bool (source_data, "muted", muted); obs_data_set_bool (source_data, "push-to-mute", push_to_mute); diff --git a/libobs/obs.h b/libobs/obs.h index 181bd6faf99adccef8e9ee609cb65716212f738f..0b587bc8b18b1b064eb61f8af74f7424a748dde8 100644 --- a/libobs/obs.h +++ b/libobs/obs.h @@ -893,6 +893,15 @@ EXPORT void obs_source_set_volume(obs_source_t *source, float volume); /** Gets the user volume for a source that has audio output */ EXPORT float obs_source_get_volume(const obs_source_t *source); +/* Gets speaker layout of a source */ +EXPORT enum speaker_layout obs_source_get_speaker_layout(obs_source_t *source); + +/** Sets the balance value for a stereo audio source */ +EXPORT void obs_source_set_balance_value(obs_source_t *source, float balance); + +/** Gets the balance value for a stereo audio source */ +EXPORT float obs_source_get_balance_value(const obs_source_t *source); + /** Sets the audio sync offset (in nanoseconds) for a source */ EXPORT void obs_source_set_sync_offset(obs_source_t *source, int64_t offset);