From bc542a3e754b6db7f185481438b9481778249ff1 Mon Sep 17 00:00:00 2001 From: Danni Date: Tue, 20 May 2014 09:26:18 -0500 Subject: [PATCH 1/8] Added simple volume meter for reference of input levels. --- libobs/media-io/audio-io.c | 60 +++++++++++++++++++++++++++----------- libobs/media-io/audio-io.h | 2 +- libobs/obs-source.c | 25 ++++++++++++++-- libobs/obs.h | 3 ++ obs/volume-control.cpp | 37 ++++++++++++++++++++++- obs/volume-control.hpp | 12 +++++--- 6 files changed, 114 insertions(+), 25 deletions(-) diff --git a/libobs/media-io/audio-io.c b/libobs/media-io/audio-io.c index d813b2eb6..5d2b9abe6 100644 --- a/libobs/media-io/audio-io.c +++ b/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; } diff --git a/libobs/media-io/audio-io.h b/libobs/media-io/audio-io.h index a08ad0d20..b0c457de5 100644 --- a/libobs/media-io/audio-io.h +++ b/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 diff --git a/libobs/obs-source.c b/libobs/obs-source.c index d6f3af7d4..80773f803 100644 --- a/libobs/obs-source.c +++ b/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) { diff --git a/libobs/obs.h b/libobs/obs.h index ad7751a56..60de5f529 100644 --- a/libobs/obs.h +++ b/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); diff --git a/obs/volume-control.cpp b/obs/volume-control.cpp index 3b5e742ca..5de818f72 100644 --- a/obs/volume-control.cpp +++ b/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(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); } diff --git a/obs/volume-control.hpp b/obs/volume-control.hpp index 2e2bef709..cabf75580 100644 --- a/obs/volume-control.hpp +++ b/obs/volume-control.hpp @@ -2,6 +2,7 @@ #include #include +#include /* 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: From 90d9a5204fd07a7a57906858dba4b3666552f880 Mon Sep 17 00:00:00 2001 From: Danni Date: Sat, 24 May 2014 16:24:48 -0700 Subject: [PATCH 2/8] Updated per comments pull #90. --- libobs/media-io/audio-io.c | 13 +++++++++---- libobs/obs-source.c | 5 +---- obs/volume-control.cpp | 4 ++-- 3 files changed, 12 insertions(+), 10 deletions(-) diff --git a/libobs/media-io/audio-io.c b/libobs/media-io/audio-io.c index 5d2b9abe6..0594b6dd0 100644 --- a/libobs/media-io/audio-io.c +++ b/libobs/media-io/audio-io.c @@ -711,6 +711,11 @@ uint32_t audio_output_samplerate(audio_t audio) return audio ? audio->info.samples_per_sec : 0; } +/* On some platforms, max() may already be defined */ +#ifndef max +#define max(a, b) ((a) > (b) ? (a) : (b)) +#endif + /* TODO: Optimization of volume multiplication functions */ static inline int mul_vol_u8bit(void *array, float volume, size_t total_num) @@ -798,15 +803,15 @@ static inline int mul_vol_float(void *array, float volume, size_t total_num) for (size_t i = 0; i < total_num; i++) { vals[i] *= volume; - maxVol = max(maxVol, (float)fabs(vals[i])); + maxVol = fmaxf(maxVol, (float)fabs(vals[i])); } return (int)(maxVol * 10000.f); } -// [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. +/* [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) diff --git a/libobs/obs-source.c b/libobs/obs-source.c index 80773f803..95ec47a02 100644 --- a/libobs/obs-source.c +++ b/libobs/obs-source.c @@ -29,8 +29,6 @@ #include "obs.h" #include "obs-internal.h" - - static inline bool source_valid(struct obs_source *source) { return source && source->context.data; @@ -82,7 +80,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)", + "void volumelevel(ptr source, in out int volume)", NULL }; @@ -1617,7 +1615,6 @@ void obs_source_updatevolumelevel(obs_source_t source, int volume) } } - static void set_tree_preset_vol(obs_source_t parent, obs_source_t child, void *param) { diff --git a/obs/volume-control.cpp b/obs/volume-control.cpp index 5de818f72..5e893faaa 100644 --- a/obs/volume-control.cpp +++ b/obs/volume-control.cpp @@ -18,8 +18,8 @@ void VolControl::OBSVolumeChanged(void *data, calldata_t calldata) } -// [Danni] This may be a bit too resource intensive for such a simple -// application. +/* [Danni] This may be a bit too resource intensive for such a simple + application. */ void VolControl::OBSVolumeLevel(void *data, calldata_t calldata) { From 9c3fb4b8dce0b7e31d3a574bf391df8a05de81ff Mon Sep 17 00:00:00 2001 From: Danni Date: Sat, 24 May 2014 16:42:54 -0700 Subject: [PATCH 3/8] Added simple volume meter. Updated per comments Pull Req #90 --- libobs/media-io/audio-io.c | 21 ++++----------------- 1 file changed, 4 insertions(+), 17 deletions(-) diff --git a/libobs/media-io/audio-io.c b/libobs/media-io/audio-io.c index 0594b6dd0..8311fb98f 100644 --- a/libobs/media-io/audio-io.c +++ b/libobs/media-io/audio-io.c @@ -711,16 +711,10 @@ uint32_t audio_output_samplerate(audio_t audio) return audio ? audio->info.samples_per_sec : 0; } -/* On some platforms, max() may already be defined */ -#ifndef max -#define max(a, b) ((a) > (b) ? (a) : (b)) -#endif - /* TODO: Optimization of volume multiplication functions */ 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); @@ -728,23 +722,20 @@ static inline int 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); + return 0; } 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); + return 0; } static inline float conv_24bit_to_float(uint8_t *vals) @@ -769,21 +760,18 @@ static inline void conv_float_to_24bit(float fval, uint8_t *vals) 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); + return 0; } 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; @@ -791,9 +779,8 @@ static inline int 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); + return 0; } static inline int mul_vol_float(void *array, float volume, size_t total_num) From 6d6bdfc0381f44b36b298033ab2f45e1b826ed98 Mon Sep 17 00:00:00 2001 From: Danni Date: Sat, 24 May 2014 16:42:54 -0700 Subject: [PATCH 4/8] Added simple volume meter. Updated per comments Pull Req #90 Missed a bit... --- obs/volume-control.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/obs/volume-control.cpp b/obs/volume-control.cpp index 5e893faaa..01fcd1605 100644 --- a/obs/volume-control.cpp +++ b/obs/volume-control.cpp @@ -79,7 +79,7 @@ VolControl::VolControl(OBSSource source_) slider->setMinimum(0); slider->setMaximum(100); slider->setValue(vol); - slider->setMaximumHeight(10); +// slider->setMaximumHeight(13); volMeter->setMaximumHeight(1); volMeter->setMinimum(0); From d891e9772c51dae3700d5f7c64bff1aa12fbf1a7 Mon Sep 17 00:00:00 2001 From: jp9000 Date: Tue, 3 Jun 2014 04:41:12 -0700 Subject: [PATCH 5/8] libobs: Fix signal handler lock bug There was a return statement within a section of code that had a locked mutex. --- libobs/callback/signal.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/libobs/callback/signal.c b/libobs/callback/signal.c index bb18a5b58..b90403fca 100644 --- a/libobs/callback/signal.c +++ b/libobs/callback/signal.c @@ -173,11 +173,14 @@ void signal_handler_connect(signal_handler_t handler, const char *signal, pthread_mutex_lock(&handler->mutex); sig = getsignal(handler, signal, &last); - if (!sig) - return; - pthread_mutex_unlock(&handler->mutex); + if (!sig) { + blog(LOG_WARNING, "signal_handler_connect: " + "signal '%s' not found", signal); + return; + } + /* -------------- */ pthread_mutex_lock(&sig->mutex); From 5cd64ce7f218ed43eeb8b67edfdb52083109338c Mon Sep 17 00:00:00 2001 From: jp9000 Date: Tue, 3 Jun 2014 04:43:00 -0700 Subject: [PATCH 6/8] libobs: Add level/magnitude/peak volume levels This replaces the older code which simply queried the max volume level value for any given audio. I'm still not 100% sure on if this is how I want to approach the problem, particularly, whether this should be done in obs_source or in audio_line, but it can always be moved later if needed. This uses the calculations by the awesome Bill Hamilton that OBS1 used for its volume levels. It calculates the current max (level), magnitude, and current peak. This data then can be used to create awesome volume meter controls later on. NOTE: Will probably need optimization, does one float at a time right now. Also, change some of the naming conventions. I actually need to change a lot of the naming conventions in general so that all words are separated by underscores. Kind of a bad practice there on my part. --- libobs/obs-internal.h | 6 +++ libobs/obs-source.c | 103 +++++++++++++++++++++++++++++++++--------- libobs/obs.c | 2 + 3 files changed, 89 insertions(+), 22 deletions(-) diff --git a/libobs/obs-internal.h b/libobs/obs-internal.h index eaf6d4faa..5f13e6943 100644 --- a/libobs/obs-internal.h +++ b/libobs/obs-internal.h @@ -276,6 +276,12 @@ struct obs_source { float present_volume; int64_t sync_offset; + /* audio levels*/ + float vol_mag; + float vol_max; + float vol_peak; + size_t vol_update_count; + /* transition volume is meant to store the sum of transitioning volumes * of a source, i.e. if a source is within both the "to" and "from" * targets of a transition, it would add both volumes to this variable, diff --git a/libobs/obs-source.c b/libobs/obs-source.c index 95ec47a02..cdf029525 100644 --- a/libobs/obs-source.c +++ b/libobs/obs-source.c @@ -80,7 +80,8 @@ 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 int volume)", + "void volume_level(ptr source, float level, float magnitude, " + "float peak)", NULL }; @@ -528,10 +529,83 @@ static inline void handle_ts_jump(obs_source_t source, uint64_t expected, /* if has video, ignore audio data until reset */ if (source->info.output_flags & OBS_SOURCE_ASYNC) os_atomic_dec_long(&source->av_sync_ref); - else + else reset_audio_timing(source, ts); } +#define VOL_MIN -96.0f +#define VOL_MAX 0.0f + +static inline float to_db(float val) +{ + float db = 20.0f * log10f(val); + return isfinite(db) ? db : VOL_MIN; +} + +static void calc_volume_levels(struct obs_source *source, float *array, + size_t frames) +{ + float sum_val = 0.0f; + float max_val = 0.0f; + float rms_val = 0.0f; + + const uint32_t sample_rate = audio_output_samplerate(obs_audio()); + const size_t channels = audio_output_channels(obs_audio()); + const size_t count = frames * channels; + const size_t vol_peak_delay = sample_rate * 3; + const float alpha = 0.15f; + + for (size_t i = 0; i < count; i++) { + float val = array[i]; + float val_pow2 = val * val; + + sum_val += val_pow2; + max_val = fmaxf(max_val, val_pow2); + } + + rms_val = to_db(sqrtf(sum_val / (float)count)); + max_val = to_db(sqrtf(max_val)); + + if (max_val > source->vol_max) + source->vol_max = max_val; + else + source->vol_max = alpha * source->vol_max + + (1.0f - alpha) * max_val; + + if (source->vol_max > source->vol_peak || + source->vol_update_count > vol_peak_delay) { + source->vol_peak = source->vol_max; + source->vol_update_count = 0; + } else { + source->vol_update_count += count; + } + + source->vol_mag = alpha * rms_val + source->vol_mag * (1.0f - alpha); +} + +/* TODO update peak/etc later */ +static void obs_source_update_volume_level(obs_source_t source, + struct audio_data *in) +{ + if (source && in) { + struct calldata data = {0}; + + calc_volume_levels(source, (float*)in->data[0], in->frames); + + calldata_setptr (&data, "source", source); + calldata_setfloat(&data, "level", source->vol_max); + calldata_setfloat(&data, "magnitude", source->vol_mag); + calldata_setfloat(&data, "peak", source->vol_peak); + + signal_handler_signal(source->context.signals, "volume_level", + &data); + signal_handler_signal(obs->signals, "source_volume_level", + &data); + + calldata_free(&data); + } +} + static void source_output_audio_line(obs_source_t source, const struct audio_data *data) { @@ -570,9 +644,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; - - int vol = audio_line_output(source->audio_line, &in); - obs_source_updatevolumelevel(source, vol); + + audio_line_output(source->audio_line, &in); + obs_source_update_volume_level(source, &in); } enum convert_type { @@ -848,13 +922,13 @@ static inline void obs_source_draw_texture(struct obs_source *source, param = effect_getparambyname(effect, "color_range_min"); effect_setval(effect, param, color_range_min, size); } - + if (color_range_max) { size_t const size = sizeof(float) * 3; param = effect_getparambyname(effect, "color_range_max"); effect_setval(effect, param, color_range_max, size); } - + if (color_matrix) { param = effect_getparambyname(effect, "color_matrix"); effect_setval(effect, param, color_matrix, sizeof(float) * 16); @@ -1600,21 +1674,6 @@ 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) { diff --git a/libobs/obs.c b/libobs/obs.c index bd0128f4d..8a694ad2c 100644 --- a/libobs/obs.c +++ b/libobs/obs.c @@ -471,6 +471,8 @@ static const char *obs_signals[] = { "void source_show(ptr source)", "void source_hide(ptr source)", "void source_volume(ptr source, in out float volume)", + "void source_volume_level(ptr source, float level, float magnitude, " + "float peak)", "void channel_change(int channel, in out ptr source, ptr prev_source)", "void master_volume(in out float volume)", From 4ccf928ea1bc604fa27477d89876fd1cfc855ef1 Mon Sep 17 00:00:00 2001 From: jp9000 Date: Tue, 3 Jun 2014 04:50:15 -0700 Subject: [PATCH 7/8] libobs/media-io: Remove obsolete mixing functions Also, Remove the volume level processing from audio-io.c, it was moved to obs_source instead. --- libobs/media-io/audio-io.c | 222 +++---------------------------------- libobs/media-io/audio-io.h | 2 +- 2 files changed, 15 insertions(+), 209 deletions(-) diff --git a/libobs/media-io/audio-io.c b/libobs/media-io/audio-io.c index 8311fb98f..d5a16e7ea 100644 --- a/libobs/media-io/audio-io.c +++ b/libobs/media-io/audio-io.c @@ -181,78 +181,9 @@ static inline size_t min_size(size_t a, size_t b) ((val > maxval) ? maxval : ((val < minval) ? minval : val)) #endif -#define MIN_S8 -128 -#define MAX_S8 127 -#define MIN_S16 -32767 -#define MAX_S16 32767 -#define MIN_S32 -2147483647 -#define MAX_S32 2147483647 - #define MIX_BUFFER_SIZE 256 /* TODO: optimize mixing */ -static void mix_u8(uint8_t *mix, struct circlebuf *buf, size_t size) -{ - uint8_t vals[MIX_BUFFER_SIZE]; - register int16_t mix_val; - - while (size) { - size_t pop_count = min_size(size, sizeof(vals)); - size -= pop_count; - - circlebuf_pop_front(buf, vals, pop_count); - - for (size_t i = 0; i < pop_count; i++) { - mix_val = (int16_t)*mix - 128; - mix_val += (int16_t)vals[i] - 128; - mix_val = CLAMP(mix_val, MIN_S8, MAX_S8) + 128; - *(mix++) = (uint8_t)mix_val; - } - } -} - -static void mix_s16(uint8_t *mix_in, struct circlebuf *buf, size_t size) -{ - int16_t *mix = (int16_t*)mix_in; - int16_t vals[MIX_BUFFER_SIZE]; - register int32_t mix_val; - - while (size) { - size_t pop_count = min_size(size, sizeof(vals)); - size -= pop_count; - - circlebuf_pop_front(buf, vals, pop_count); - pop_count /= sizeof(int16_t); - - for (size_t i = 0; i < pop_count; i++) { - mix_val = (int32_t)*mix; - mix_val += (int32_t)vals[i]; - *(mix++) = (int16_t)CLAMP(mix_val, MIN_S16, MAX_S16); - } - } -} - -static void mix_s32(uint8_t *mix_in, struct circlebuf *buf, size_t size) -{ - int32_t *mix = (int32_t*)mix_in; - int32_t vals[MIX_BUFFER_SIZE]; - register int64_t mix_val; - - while (size) { - size_t pop_count = min_size(size, sizeof(vals)); - size -= pop_count; - - circlebuf_pop_front(buf, vals, pop_count); - pop_count /= sizeof(int32_t); - - for (size_t i = 0; i < pop_count; i++) { - mix_val = (int64_t)*mix; - mix_val += (int64_t)vals[i]; - *(mix++) = (int32_t)CLAMP(mix_val, MIN_S32, MAX_S32); - } - } -} - static void mix_float(uint8_t *mix_in, struct circlebuf *buf, size_t size) { float *mix = (float*)mix_in; @@ -273,31 +204,6 @@ static void mix_float(uint8_t *mix_in, struct circlebuf *buf, size_t size) } } -static inline void mix_audio(enum audio_format format, - uint8_t *mix, struct circlebuf *buf, size_t size) -{ - switch (format) { - case AUDIO_FORMAT_UNKNOWN: - break; - - case AUDIO_FORMAT_U8BIT: - case AUDIO_FORMAT_U8BIT_PLANAR: - mix_u8(mix, buf, size); break; - - case AUDIO_FORMAT_16BIT: - case AUDIO_FORMAT_16BIT_PLANAR: - mix_s16(mix, buf, size); break; - - case AUDIO_FORMAT_32BIT: - case AUDIO_FORMAT_32BIT_PLANAR: - mix_s32(mix, buf, size); break; - - case AUDIO_FORMAT_FLOAT: - case AUDIO_FORMAT_FLOAT_PLANAR: - mix_float(mix, buf, size); break; - } -} - static inline bool mix_audio_line(struct audio_output *audio, struct audio_line *line, size_t size, uint64_t timestamp) { @@ -315,8 +221,7 @@ static inline bool mix_audio_line(struct audio_output *audio, for (size_t i = 0; i < audio->planes; i++) { size_t pop_size = min_size(size, line->buffers[i].size); - mix_audio(audio->info.format, - audio->mix_buffers[i].array + time_offset, + mix_float(audio->mix_buffers[i].array + time_offset, &line->buffers[i], pop_size); } @@ -711,100 +616,16 @@ uint32_t audio_output_samplerate(audio_t audio) return audio ? audio->info.samples_per_sec : 0; } -/* TODO: Optimization of volume multiplication functions */ - -static inline int mul_vol_u8bit(void *array, float volume, size_t total_num) +/* TODO: optimize these two functions */ +static inline void mul_vol_float(float *array, float volume, size_t count) { - uint8_t *vals = array; - int32_t vol = (int32_t)(volume * 127.0f); - - for (size_t i = 0; i < total_num; i++) { - 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); - } - return 0; + for (size_t i = 0; i < count; i++) + array[i] *= volume; } -static inline int mul_vol_16bit(void *array, float volume, size_t total_num) -{ - 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); - } - return 0; -} - -static inline float conv_24bit_to_float(uint8_t *vals) -{ - int32_t val = ((int32_t)vals[0]) | - ((int32_t)vals[1] << 8) | - ((int32_t)vals[2] << 16); - - if ((val & 0x800000) != 0) - val |= 0xFF000000; - - return (float)val / 8388607.0f; -} - -static inline void conv_float_to_24bit(float fval, uint8_t *vals) -{ - int32_t val = (int32_t)(fval * 8388607.0f); - vals[0] = (val) & 0xFF; - vals[1] = (val >> 8) & 0xFF; - vals[2] = (val >> 16) & 0xFF; -} - -static inline int mul_vol_24bit(void *array, float volume, size_t total_num) -{ - 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; - } - return 0; -} - -static inline int mul_vol_32bit(void *array, float volume, size_t total_num) -{ - int32_t *vals = array; - double dvol = (double)volume; - - for (size_t i = 0; i < total_num; i++) { - double val = (double)vals[i] / 2147483647.0; - double output = val * dvol; - vals[i] = (int32_t)(CLAMP(output, -1.0, 1.0) * 2147483647.0); - } - return 0; -} - -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++) { - vals[i] *= volume; - maxVol = fmaxf(maxVol, (float)fabs(vals[i])); - } - - return (int)(maxVol * 10000.f); -} - -/* [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, +static void 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; @@ -816,35 +637,22 @@ static int audio_line_place_data_pos(struct audio_line *line, uint8_t *array = line->volume_buffers[i].array; switch (line->audio->info.format) { - case AUDIO_FORMAT_U8BIT: - case AUDIO_FORMAT_U8BIT_PLANAR: - maxVol = mul_vol_u8bit(array, data->volume, total_num); - break; - case AUDIO_FORMAT_16BIT: - case AUDIO_FORMAT_16BIT_PLANAR: - maxVol = mul_vol_16bit(array, data->volume, total_num); - break; - case AUDIO_FORMAT_32BIT: - case AUDIO_FORMAT_32BIT_PLANAR: - maxVol = mul_vol_32bit(array, data->volume, total_num); - break; case AUDIO_FORMAT_FLOAT: case AUDIO_FORMAT_FLOAT_PLANAR: - maxVol = mul_vol_float(array, data->volume, total_num); + mul_vol_float((float*)array, data->volume, total_num); break; case AUDIO_FORMAT_UNKNOWN: blog(LOG_ERROR, "audio_line_place_data_pos: " - "Unknown format"); + "Unsupported or unknown format"); break; } circlebuf_place(&line->buffers[i], position, line->volume_buffers[i].array, total_size); } - return maxVol; } -static int audio_line_place_data(struct audio_line *line, +static void audio_line_place_data(struct audio_line *line, const struct audio_data *data) { size_t pos = ts_diff_bytes(line->audio, data->timestamp, @@ -858,26 +666,25 @@ static int audio_line_place_data(struct audio_line *line, line->buffers[0].size); #endif - return audio_line_place_data_pos(line, data, pos); + audio_line_place_data_pos(line, data, pos); } -int audio_line_output(audio_line_t line, const struct audio_data *data) +void 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 0; + if (!line || !data) return; - int maxVol = 0; pthread_mutex_lock(&line->mutex); if (!line->buffers[0].size) { line->base_timestamp = data->timestamp - line->audio->info.buffer_ms * 1000000; - maxVol = audio_line_place_data(line, data); + audio_line_place_data(line, data); } else if (line->base_timestamp <= data->timestamp) { - maxVol = audio_line_place_data(line, data); + audio_line_place_data(line, data); } else { blog(LOG_DEBUG, "Bad timestamp for audio line '%s', " @@ -889,5 +696,4 @@ int audio_line_output(audio_line_t line, const struct audio_data *data) } pthread_mutex_unlock(&line->mutex); - return maxVol; } diff --git a/libobs/media-io/audio-io.h b/libobs/media-io/audio-io.h index b0c457de5..a08ad0d20 100644 --- a/libobs/media-io/audio-io.h +++ b/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 int audio_line_output(audio_line_t line, const struct audio_data *data); +EXPORT void audio_line_output(audio_line_t line, const struct audio_data *data); #ifdef __cplusplus From ab1543977f0b8bcf389cb138e2ab864e41e6a3ff Mon Sep 17 00:00:00 2001 From: jp9000 Date: Tue, 3 Jun 2014 04:51:27 -0700 Subject: [PATCH 8/8] UI: Add new volume meter handling This adds support for the more smooth volume levels that accounts for both level and magnitude. Currently, it just averages level and magnitude, later on a full audio meter control can be made that would properly utilize level, magnitude, and peak. Also cleaned up the code a bit and removed some trailing whitespace. --- obs/volume-control.cpp | 79 +++++++++++++++++++++++++++++++++--------- obs/volume-control.hpp | 5 ++- 2 files changed, 66 insertions(+), 18 deletions(-) diff --git a/obs/volume-control.cpp b/obs/volume-control.cpp index 01fcd1605..6f6799d40 100644 --- a/obs/volume-control.cpp +++ b/obs/volume-control.cpp @@ -1,33 +1,57 @@ #include "volume-control.hpp" #include "qt-wrappers.hpp" +#include #include #include #include #include #include +#include using namespace std; +#define VOL_MIN -96.0f +#define VOL_MAX 0.0f + +#define VOL_MIN_LOG -2.0086001717619175 +#define VOL_MAX_LOG -0.77815125038364363 + +#define UPDATE_INTERVAL_MS 50 + +static inline float DBToLog(float db) +{ + return -log10f(0.0f - (db - 6.0f)); +} + +static inline float DBToLinear(float db_full) +{ + float db = fmaxf(fminf(db_full, VOL_MAX), VOL_MIN); + return (DBToLog(db) - VOL_MIN_LOG) / (VOL_MAX_LOG - VOL_MIN_LOG); +} + void VolControl::OBSVolumeChanged(void *data, calldata_t calldata) { VolControl *volControl = static_cast(data); int vol = (int)(calldata_float(calldata, "volume") * 100.0f + 0.5f); - QMetaObject::invokeMethod(volControl, "VolumeChanged", - Q_ARG(int, vol)); + QMetaObject::invokeMethod(volControl, "VolumeChanged", 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(data); - int v = calldata_int(calldata, "volumelevel"); - + float level = calldata_float(calldata, "level"); + float mag = calldata_float(calldata, "magnitude"); + + /* + * TODO: an actual volume control that can process level, mag, peak. + * + * for the time being, just average level and magnitude. + */ + float result = (level + mag) * 0.5f; + QMetaObject::invokeMethod(volControl, "VolumeLevel", - Q_ARG(int, v)); + Q_ARG(float, result)); } void VolControl::VolumeChanged(int vol) @@ -37,9 +61,23 @@ void VolControl::VolumeChanged(int vol) signalChanged = true; } -void VolControl::VolumeLevel(int vol) +void VolControl::VolumeLevel(float level) { - volMeter->setValue(vol); /* linear */ + uint64_t curMeterTime = os_gettime_ns() / 1000000; + + levelTotal += level; + levelCount += 1.0f; + + /* only update after a certain amount of time */ + if ((curMeterTime - lastMeterTime) > UPDATE_INTERVAL_MS) { + lastMeterTime = curMeterTime; + + float finalLevel = levelTotal / levelCount; + volMeter->setValue(int(DBToLinear(finalLevel) * 10000.0f)); + + levelTotal = 0.0f; + levelCount = 0.0f; + } } void VolControl::SliderChanged(int vol) @@ -53,12 +91,16 @@ void VolControl::SliderChanged(int vol) signal_handler_connect(obs_source_signalhandler(source), "volume", OBSVolumeChanged, this); } + volLabel->setText(QString::number(vol)); } VolControl::VolControl(OBSSource source_) : source (source_), - signalChanged (true) + signalChanged (true), + lastMeterTime (0), + levelTotal (0.0f), + levelCount (0.0f) { QVBoxLayout *mainLayout = new QVBoxLayout(); QHBoxLayout *textLayout = new QHBoxLayout(); @@ -86,10 +128,13 @@ VolControl::VolControl(OBSSource source_) volMeter->setMaximum(10000); volMeter->setTextVisible(false); - // [Danni] Temporary color. - QString testColor = "QProgressBar {border: 0px} QProgressBar::chunk {width: 1px; background-color: #AA0000;}"; + /* [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); @@ -108,7 +153,7 @@ VolControl::VolControl(OBSSource source_) "volume", OBSVolumeChanged, this); signal_handler_connect(obs_source_signalhandler(source), - "volumelevel", OBSVolumeLevel, this); + "volume_level", OBSVolumeLevel, this); QWidget::connect(slider, SIGNAL(valueChanged(int)), this, SLOT(SliderChanged(int))); @@ -120,5 +165,5 @@ VolControl::~VolControl() "volume", OBSVolumeChanged, this); signal_handler_disconnect(obs_source_signalhandler(source), - "volumelevel", OBSVolumeLevel, this); + "volume_level", OBSVolumeLevel, this); } diff --git a/obs/volume-control.hpp b/obs/volume-control.hpp index cabf75580..ffc0ceaa3 100644 --- a/obs/volume-control.hpp +++ b/obs/volume-control.hpp @@ -19,13 +19,16 @@ private: QProgressBar *volMeter; QSlider *slider; bool signalChanged; + uint64_t lastMeterTime; + float levelTotal; + float levelCount; 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 VolumeLevel(float level); void SliderChanged(int vol); public: