Browse Source

Added simple volume meter for reference of input levels.

Danni 11 years ago
parent
commit
bc542a3e75
6 changed files with 114 additions and 25 deletions
  1. 43 17
      libobs/media-io/audio-io.c
  2. 1 1
      libobs/media-io/audio-io.h
  3. 23 2
      libobs/obs-source.c
  4. 3 0
      libobs/obs.h
  5. 36 1
      obs/volume-control.cpp
  6. 8 4
      obs/volume-control.hpp

+ 43 - 17
libobs/media-io/audio-io.c

@@ -713,8 +713,9 @@ uint32_t audio_output_samplerate(audio_t audio)
 
 /* TODO: Optimization of volume multiplication functions */
 
-static inline void mul_vol_u8bit(void *array, float volume, size_t total_num)
+static inline int mul_vol_u8bit(void *array, float volume, size_t total_num)
 {
+	int maxVol = 0;
 	uint8_t *vals = array;
 	int32_t vol = (int32_t)(volume * 127.0f);
 
@@ -722,18 +723,23 @@ static inline void mul_vol_u8bit(void *array, float volume, size_t total_num)
 		int32_t val = (int32_t)vals[i] - 128;
 		int32_t output = val * vol / 127;
 		vals[i] = (uint8_t)(CLAMP(output, MIN_S8, MAX_S8) + 128);
+		maxVol = max(maxVol, abs(vals[i]));
 	}
+	return maxVol * (10000 / MAX_S8);
 }
 
-static inline void mul_vol_16bit(void *array, float volume, size_t total_num)
+static inline int mul_vol_16bit(void *array, float volume, size_t total_num)
 {
+	int maxVol = 0;
 	uint16_t *vals = array;
 	int64_t vol = (int64_t)(volume * 32767.0f);
 
 	for (size_t i = 0; i < total_num; i++) {
 		int64_t output = (int64_t)vals[i] * vol / 32767;
 		vals[i] = (int32_t)CLAMP(output, MIN_S16, MAX_S16);
+		maxVol = max(maxVol, abs(vals[i]));
 	}
+	return maxVol * (10000 / MAX_S16);
 }
 
 static inline float conv_24bit_to_float(uint8_t *vals)
@@ -756,19 +762,23 @@ static inline void conv_float_to_24bit(float fval, uint8_t *vals)
 	vals[2] = (val >> 16) & 0xFF;
 }
 
-static inline void mul_vol_24bit(void *array, float volume, size_t total_num)
+static inline int mul_vol_24bit(void *array, float volume, size_t total_num)
 {
+	float maxVol = 0.f;
 	uint8_t *vals = array;
 
 	for (size_t i = 0; i < total_num; i++) {
 		float val = conv_24bit_to_float(vals) * volume;
 		conv_float_to_24bit(CLAMP(val, -1.0f, 1.0f), vals);
 		vals += 3;
+		maxVol = max(maxVol, (float)fabs(val));
 	}
+	return (int) (maxVol * 10000.f);
 }
 
-static inline void mul_vol_32bit(void *array, float volume, size_t total_num)
+static inline int mul_vol_32bit(void *array, float volume, size_t total_num)
 {
+	int maxVol = 0;
 	int32_t *vals = array;
 	double dvol = (double)volume;
 
@@ -776,20 +786,33 @@ static inline void mul_vol_32bit(void *array, float volume, size_t total_num)
 		double val = (double)vals[i] / 2147483647.0;
 		double output = val * dvol;
 		vals[i] = (int32_t)(CLAMP(output, -1.0, 1.0) * 2147483647.0);
+		maxVol = max(maxVol, abs(vals[i])); 
 	}
+	return maxVol * (10000 / MAX_S32);
 }
 
-static inline void mul_vol_float(void *array, float volume, size_t total_num)
+static inline int mul_vol_float(void *array, float volume, size_t total_num)
 {
+	float maxVol = 0; 
 	float *vals = array;
 
-	for (size_t i = 0; i < total_num; i++)
+	for (size_t i = 0; i < total_num; i++) {
 		vals[i] *= volume;
+		maxVol = max(maxVol, (float)fabs(vals[i]));
+	}
+
+	return (int)(maxVol * 10000.f);
 }
 
-static void audio_line_place_data_pos(struct audio_line *line,
+// [Danni] changed to int for volume feedback. Seems like the most logical 
+//		   place to calculate this to avoid unnessisary iterations.
+//		   scaled to max of 10000.
+
+static int audio_line_place_data_pos(struct audio_line *line,
 		const struct audio_data *data, size_t position)
 {
+	int maxVol = 0;
+
 	bool   planar     = line->audio->planes > 1;
 	size_t total_num  = data->frames * (planar ? 1 : line->audio->channels);
 	size_t total_size = data->frames * line->audio->block_size;
@@ -803,19 +826,19 @@ static void audio_line_place_data_pos(struct audio_line *line,
 		switch (line->audio->info.format) {
 		case AUDIO_FORMAT_U8BIT:
 		case AUDIO_FORMAT_U8BIT_PLANAR:
-			mul_vol_u8bit(array, data->volume, total_num);
+			maxVol = mul_vol_u8bit(array, data->volume, total_num);
 			break;
 		case AUDIO_FORMAT_16BIT:
 		case AUDIO_FORMAT_16BIT_PLANAR:
-			mul_vol_16bit(array, data->volume, total_num);
+			maxVol = mul_vol_16bit(array, data->volume, total_num);
 			break;
 		case AUDIO_FORMAT_32BIT:
 		case AUDIO_FORMAT_32BIT_PLANAR:
-			mul_vol_32bit(array, data->volume, total_num);
+			maxVol = mul_vol_32bit(array, data->volume, total_num);
 			break;
 		case AUDIO_FORMAT_FLOAT:
 		case AUDIO_FORMAT_FLOAT_PLANAR:
-			mul_vol_float(array, data->volume, total_num);
+			maxVol = mul_vol_float(array, data->volume, total_num);
 			break;
 		case AUDIO_FORMAT_UNKNOWN:
 			blog(LOG_ERROR, "audio_line_place_data_pos: "
@@ -826,9 +849,10 @@ static void audio_line_place_data_pos(struct audio_line *line,
 		circlebuf_place(&line->buffers[i], position,
 				line->volume_buffers[i].array, total_size);
 	}
+	return maxVol;
 }
 
-static void audio_line_place_data(struct audio_line *line,
+static int audio_line_place_data(struct audio_line *line,
 		const struct audio_data *data)
 {
 	size_t pos = ts_diff_bytes(line->audio, data->timestamp,
@@ -842,25 +866,26 @@ static void audio_line_place_data(struct audio_line *line,
 			line->buffers[0].size);
 #endif
 
-	audio_line_place_data_pos(line, data, pos);
+	return audio_line_place_data_pos(line, data, pos);
 }
 
-void audio_line_output(audio_line_t line, const struct audio_data *data)
+int audio_line_output(audio_line_t line, const struct audio_data *data)
 {
 	/* TODO: prevent insertation of data too far away from expected
 	 * audio timing */
 
-	if (!line || !data) return;
+	if (!line || !data) return 0;
 
+	int maxVol = 0;
 	pthread_mutex_lock(&line->mutex);
 
 	if (!line->buffers[0].size) {
 		line->base_timestamp = data->timestamp -
 		                       line->audio->info.buffer_ms * 1000000;
-		audio_line_place_data(line, data);
+		maxVol = audio_line_place_data(line, data);
 
 	} else if (line->base_timestamp <= data->timestamp) {
-		audio_line_place_data(line, data);
+		maxVol = audio_line_place_data(line, data);
 
 	} else {
 		blog(LOG_DEBUG, "Bad timestamp for audio line '%s', "
@@ -872,4 +897,5 @@ void audio_line_output(audio_line_t line, const struct audio_data *data)
 	}
 
 	pthread_mutex_unlock(&line->mutex);
+	return maxVol;
 }

+ 1 - 1
libobs/media-io/audio-io.h

@@ -190,7 +190,7 @@ EXPORT const struct audio_output_info *audio_output_getinfo(audio_t audio);
 
 EXPORT audio_line_t audio_output_createline(audio_t audio, const char *name);
 EXPORT void audio_line_destroy(audio_line_t line);
-EXPORT void audio_line_output(audio_line_t line, const struct audio_data *data);
+EXPORT int audio_line_output(audio_line_t line, const struct audio_data *data);
 
 
 #ifdef __cplusplus

+ 23 - 2
libobs/obs-source.c

@@ -19,6 +19,7 @@
 
 #include "media-io/format-conversion.h"
 #include "media-io/video-frame.h"
+#include "media-io/audio-io.h"
 #include "util/threading.h"
 #include "util/platform.h"
 #include "callback/calldata.h"
@@ -28,6 +29,8 @@
 #include "obs.h"
 #include "obs-internal.h"
 
+
+
 static inline bool source_valid(struct obs_source *source)
 {
 	return source && source->context.data;
@@ -79,6 +82,7 @@ static const char *source_signals[] = {
 	"void show(ptr source)",
 	"void hide(ptr source)",
 	"void volume(ptr source, in out float volume)",
+	"void volumelevel(ptr source, in out float volume)",
 	NULL
 };
 
@@ -568,8 +572,9 @@ static void source_output_audio_line(obs_source_t source,
 	in.timestamp += source->timing_adjust + source->sync_offset;
 	in.volume = source->user_volume * source->present_volume *
 		obs->audio.user_volume * obs->audio.present_volume;
-
-	audio_line_output(source->audio_line, &in);
+	
+	int vol = audio_line_output(source->audio_line, &in);
+	obs_source_updatevolumelevel(source, vol);
 }
 
 enum convert_type {
@@ -1597,6 +1602,22 @@ void obs_source_setvolume(obs_source_t source, float volume)
 	}
 }
 
+void obs_source_updatevolumelevel(obs_source_t source, int volume)
+{
+	if (source) {
+		struct calldata data = { 0 };
+		calldata_setptr(&data, "source", source);
+		calldata_setint(&data, "volumelevel", volume);
+
+		signal_handler_signal(source->context.signals, "volumelevel", &data);
+		signal_handler_signal(obs->signals, "source_volumelevel", &data);
+
+		volume = (int)calldata_int(&data, "volumelevel");
+		calldata_free(&data);
+	}
+}
+
+
 static void set_tree_preset_vol(obs_source_t parent, obs_source_t child,
 		void *param)
 {

+ 3 - 0
libobs/obs.h

@@ -554,6 +554,9 @@ EXPORT proc_handler_t obs_source_prochandler(obs_source_t source);
 /** Sets the user volume for a source that has audio output */
 EXPORT void obs_source_setvolume(obs_source_t source, float volume);
 
+/** Updates live volume for a source */
+EXPORT void obs_source_updatevolumelevel(obs_source_t source, int volume);
+
 /** Sets the presentation volume for a source */
 EXPORT void obs_source_set_present_volume(obs_source_t source, float volume);
 

+ 36 - 1
obs/volume-control.cpp

@@ -17,6 +17,19 @@ void VolControl::OBSVolumeChanged(void *data, calldata_t calldata)
 			Q_ARG(int, vol));
 }
 
+
+// [Danni] This may be a bit too resource intensive for such a simple 
+//		   application.
+
+void VolControl::OBSVolumeLevel(void *data, calldata_t calldata)
+{
+	VolControl *volControl = static_cast<VolControl*>(data);
+	int v = calldata_int(calldata, "volumelevel");
+		
+	QMetaObject::invokeMethod(volControl, "VolumeLevel",
+		Q_ARG(int, v));
+}
+
 void VolControl::VolumeChanged(int vol)
 {
 	signalChanged = false;
@@ -24,6 +37,11 @@ void VolControl::VolumeChanged(int vol)
 	signalChanged = true;
 }
 
+void VolControl::VolumeLevel(int vol)
+{
+	volMeter->setValue(vol); /* linear */
+}
+
 void VolControl::SliderChanged(int vol)
 {
 	if (signalChanged) {
@@ -48,6 +66,7 @@ VolControl::VolControl(OBSSource source_)
 
 	nameLabel = new QLabel();
 	volLabel  = new QLabel();
+	volMeter  = new QProgressBar();
 	slider    = new QSlider(Qt::Horizontal);
 
 	QFont font = nameLabel->font();
@@ -60,8 +79,17 @@ VolControl::VolControl(OBSSource source_)
 	slider->setMinimum(0);
 	slider->setMaximum(100);
 	slider->setValue(vol);
-	//slider->setMaximumHeight(16);
+	slider->setMaximumHeight(10);
 
+	volMeter->setMaximumHeight(1);
+	volMeter->setMinimum(0);
+	volMeter->setMaximum(10000);
+	volMeter->setTextVisible(false);
+
+	// [Danni] Temporary color.
+	QString testColor = "QProgressBar {border: 0px} QProgressBar::chunk {width: 1px; background-color: #AA0000;}";
+	volMeter->setStyleSheet(testColor);
+	
 	textLayout->setContentsMargins(0, 0, 0, 0);
 	textLayout->addWidget(nameLabel);
 	textLayout->addWidget(volLabel);
@@ -71,6 +99,7 @@ VolControl::VolControl(OBSSource source_)
 	mainLayout->setContentsMargins(4, 4, 4, 4);
 	mainLayout->setSpacing(2);
 	mainLayout->addItem(textLayout);
+	mainLayout->addWidget(volMeter);
 	mainLayout->addWidget(slider);
 
 	setLayout(mainLayout);
@@ -78,6 +107,9 @@ VolControl::VolControl(OBSSource source_)
 	signal_handler_connect(obs_source_signalhandler(source),
 			"volume", OBSVolumeChanged, this);
 
+	signal_handler_connect(obs_source_signalhandler(source),
+		"volumelevel", OBSVolumeLevel, this);
+
 	QWidget::connect(slider, SIGNAL(valueChanged(int)),
 			this, SLOT(SliderChanged(int)));
 }
@@ -86,4 +118,7 @@ VolControl::~VolControl()
 {
 	signal_handler_disconnect(obs_source_signalhandler(source),
 			"volume", OBSVolumeChanged, this);
+
+	signal_handler_disconnect(obs_source_signalhandler(source),
+		"volumelevel", OBSVolumeLevel, this);
 }

+ 8 - 4
obs/volume-control.hpp

@@ -2,6 +2,7 @@
 
 #include <obs.hpp>
 #include <QWidget>
+#include <QProgressBar>
 
 /* TODO: Make a real volume control that isn't terrible */
 
@@ -13,15 +14,18 @@ class VolControl : public QWidget {
 
 private:
 	OBSSource source;
-	QLabel    *nameLabel;
-	QLabel    *volLabel;
-	QSlider   *slider;
-	bool      signalChanged;
+	QLabel          *nameLabel;
+	QLabel          *volLabel;
+	QProgressBar    *volMeter;
+	QSlider         *slider;
+	bool            signalChanged;
 
 	static void OBSVolumeChanged(void *param, calldata_t calldata);
+	static void OBSVolumeLevel(void *data, calldata_t calldata);
 
 private slots:
 	void VolumeChanged(int vol);
+	void VolumeLevel(int vol);
 	void SliderChanged(int vol);
 
 public: