Преглед изворни кода

UI: Implement stereo balancing

cg2121 пре 8 година
родитељ
комит
a4a7deeed6

+ 45 - 20
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<OBSBasic*>(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)

+ 3 - 3
UI/adv-audio-control.hpp

@@ -19,12 +19,12 @@ private:
 
 	QPointer<QWidget>      forceMonoContainer;
 	QPointer<QWidget>      mixerContainer;
-	QPointer<QWidget>      panningContainer;
+	QPointer<QWidget>      balanceContainer;
 
 	QPointer<QLabel>       nameLabel;
 	QPointer<QSpinBox>     volume;
 	QPointer<QCheckBox>    forceMono;
-	QPointer<QSlider>      panning;
+	QPointer<QSlider>      balance;
 	QPointer<QLabel>       labelL;
 	QPointer<QLabel>       labelR;
 	QPointer<QSpinBox>     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);

+ 1 - 1
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"

+ 1 - 1
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"));

+ 1 - 0
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;

+ 62 - 0
libobs/obs-source.c

@@ -16,6 +16,7 @@
 ******************************************************************************/
 
 #include <inttypes.h>
+#include <math.h>
 
 #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;
+}

+ 5 - 0
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

+ 7 - 0
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);

+ 9 - 0
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);