diff --git a/libobs/obs-audio-controls.c b/libobs/obs-audio-controls.c index 08b825938..1c70f2df2 100644 --- a/libobs/obs-audio-controls.c +++ b/libobs/obs-audio-controls.c @@ -46,11 +46,27 @@ struct obs_fader { bool ignore_next_signal; }; +struct obs_volmeter { + pthread_mutex_t mutex; + signal_handler_t *signals; + obs_fader_conversion_t pos_to_db; + obs_fader_conversion_t db_to_pos; + obs_source_t *source; + enum obs_fader_type type; + float cur_db; +}; + static const char *fader_signals[] = { "void volume_changed(ptr fader, float db)", NULL }; +static const char *volmeter_signals[] = { + "void levels_updated(ptr volmeter, float level, " + "float magnitude, float peak)", + NULL +}; + static inline float mul_to_db(const float mul) { return (mul == 0.0f) ? -INFINITY : 20.0f * log10f(mul); @@ -96,6 +112,24 @@ static void signal_volume_changed(signal_handler_t *sh, calldata_free(&data); } +static void signal_levels_updated(signal_handler_t *sh, + struct obs_volmeter *volmeter, + const float level, const float magnitude, const float peak) +{ + struct calldata data; + + calldata_init(&data); + + calldata_set_ptr (&data, "volmeter", volmeter); + calldata_set_float(&data, "level", level); + calldata_set_float(&data, "magnitude", magnitude); + calldata_set_float(&data, "peak", peak); + + signal_handler_signal(sh, "levels_updated", &data); + + calldata_free(&data); +} + static void fader_source_volume_changed(void *vptr, calldata_t *calldata) { struct obs_fader *fader = (struct obs_fader *) vptr; @@ -118,6 +152,18 @@ static void fader_source_volume_changed(void *vptr, calldata_t *calldata) signal_volume_changed(sh, fader, db); } +static void volmeter_source_volume_changed(void *vptr, calldata_t *calldata) +{ + struct obs_volmeter *volmeter = (struct obs_volmeter *) vptr; + + pthread_mutex_lock(&volmeter->mutex); + + float mul = (float) calldata_float(calldata, "volume"); + volmeter->cur_db = mul_to_db(mul); + + pthread_mutex_unlock(&volmeter->mutex); +} + static void fader_source_destroyed(void *vptr, calldata_t *calldata) { UNUSED_PARAMETER(calldata); @@ -126,6 +172,37 @@ static void fader_source_destroyed(void *vptr, calldata_t *calldata) obs_fader_detach_source(fader); } +static void volmeter_source_volume_levels(void *vptr, calldata_t *calldata) +{ + struct obs_volmeter *volmeter = (struct obs_volmeter *) vptr; + + pthread_mutex_lock(&volmeter->mutex); + + float mul = db_to_mul(volmeter->cur_db); + + float level = (float) calldata_float(calldata, "level"); + float magnitude = (float) calldata_float(calldata, "magnitude"); + float peak = (float) calldata_float(calldata, "peak"); + + level = volmeter->db_to_pos(mul_to_db(level * mul)); + magnitude = volmeter->db_to_pos(mul_to_db(magnitude * mul)); + peak = volmeter->db_to_pos(mul_to_db(peak * mul)); + + signal_handler_t *sh = volmeter->signals; + + pthread_mutex_unlock(&volmeter->mutex); + + signal_levels_updated(sh, volmeter, level, magnitude, peak); +} + +static void volmeter_source_destroyed(void *vptr, calldata_t *calldata) +{ + UNUSED_PARAMETER(calldata); + struct obs_volmeter *volmeter = (struct obs_volmeter *) vptr; + + obs_volmeter_detach_source(volmeter); +} + obs_fader_t *obs_fader_create(enum obs_fader_type type) { struct obs_fader *fader = bzalloc(sizeof(struct obs_fader)); @@ -309,3 +386,106 @@ signal_handler_t *obs_fader_get_signal_handler(obs_fader_t *fader) return (fader) ? fader->signals : NULL; } +obs_volmeter_t *obs_volmeter_create(enum obs_fader_type type) +{ + struct obs_volmeter *volmeter = bzalloc(sizeof(struct obs_volmeter)); + if (!volmeter) + return NULL; + + pthread_mutex_init_value(&volmeter->mutex); + if (pthread_mutex_init(&volmeter->mutex, NULL) != 0) + goto fail; + volmeter->signals = signal_handler_create(); + if (!volmeter->signals) + goto fail; + if (!signal_handler_add_array(volmeter->signals, volmeter_signals)) + goto fail; + + /* set conversion functions */ + switch(type) { + case OBS_FADER_CUBIC: + volmeter->pos_to_db = cubic_def_to_db; + volmeter->db_to_pos = cubic_db_to_def; + break; + default: + goto fail; + break; + } + volmeter->type = type; + + return volmeter; +fail: + obs_volmeter_destroy(volmeter); + return NULL; +} + +void obs_volmeter_destroy(obs_volmeter_t *volmeter) +{ + if (!volmeter) + return; + + obs_volmeter_detach_source(volmeter); + signal_handler_destroy(volmeter->signals); + pthread_mutex_destroy(&volmeter->mutex); + + bfree(volmeter); +} + +bool obs_volmeter_attach_source(obs_volmeter_t *volmeter, obs_source_t *source) +{ + signal_handler_t *sh; + + if (!volmeter || !source) + return false; + + obs_volmeter_detach_source(volmeter); + + pthread_mutex_lock(&volmeter->mutex); + + sh = obs_source_get_signal_handler(source); + signal_handler_connect(sh, "volume", + volmeter_source_volume_changed, volmeter); + signal_handler_connect(sh, "volume_level", + volmeter_source_volume_levels, volmeter); + signal_handler_connect(sh, "destroy", + volmeter_source_destroyed, volmeter); + + volmeter->source = source; + volmeter->cur_db = mul_to_db(obs_source_get_volume(source)); + + pthread_mutex_unlock(&volmeter->mutex); + + return true; +} + +void obs_volmeter_detach_source(obs_volmeter_t *volmeter) +{ + signal_handler_t *sh; + + if (!volmeter) + return; + + pthread_mutex_lock(&volmeter->mutex); + + if (!volmeter->source) + goto exit; + + sh = obs_source_get_signal_handler(volmeter->source); + signal_handler_disconnect(sh, "volume", + volmeter_source_volume_changed, volmeter); + signal_handler_disconnect(sh, "volume_level", + volmeter_source_volume_levels, volmeter); + signal_handler_disconnect(sh, "destroy", + volmeter_source_destroyed, volmeter); + + volmeter->source = NULL; + +exit: + pthread_mutex_unlock(&volmeter->mutex); +} + +signal_handler_t *obs_volmeter_get_signal_handler(obs_volmeter_t *volmeter) +{ + return (volmeter) ? volmeter->signals : NULL; +} + diff --git a/libobs/obs-audio-controls.h b/libobs/obs-audio-controls.h index e0eeefe9a..bead3dc45 100644 --- a/libobs/obs-audio-controls.h +++ b/libobs/obs-audio-controls.h @@ -137,6 +137,53 @@ EXPORT void obs_fader_detach_source(obs_fader_t *fader); */ EXPORT signal_handler_t *obs_fader_get_signal_handler(obs_fader_t *fader); +/** + * @brief Create a volume meter + * @param type the mapping type to use for the volume meter + * @return pointer to the volume meter object + * + * A volume meter object is used to prepare the sound levels reported by audio + * sources for display in a GUI. + * It will automatically take source volume into account and map the levels + * to a range [0.0f, 1.0f]. + */ +EXPORT obs_volmeter_t *obs_volmeter_create(enum obs_fader_type type); + +/** + * @brief Destroy a volume meter + * @param volmeter pointer to the volmeter object + * + * Destroy the volume meter and free all related data + */ +EXPORT void obs_volmeter_destroy(obs_volmeter_t *volmeter); + +/** + * @brief Attach the volume meter to a source + * @param volmeter pointer to the volume meter object + * @param source pointer to the source object + * @return true on success + * + * When the volume meter is attached to a source it will start to listen to + * volume updates on the source and after preparing the data emit its own + * signal. + */ +EXPORT bool obs_volmeter_attach_source(obs_volmeter_t *volmeter, + obs_source_t *source); + +/** + * @brief Detach the volume meter from the currently attached source + * @param volmeter pointer to the volume meter object + */ +EXPORT void obs_volmeter_detach_source(obs_volmeter_t *volmeter); + +/** + * @brief Get signal handler for the volume meter object + * @param volmeter pointer to the volume meter object + * @return signal handler + */ +EXPORT signal_handler_t *obs_volmeter_get_signal_handler( + obs_volmeter_t *volmeter); + #ifdef __cplusplus } #endif diff --git a/libobs/obs.h b/libobs/obs.h index 04a7b4738..7f7388bb4 100644 --- a/libobs/obs.h +++ b/libobs/obs.h @@ -48,6 +48,7 @@ struct obs_encoder; struct obs_service; struct obs_module; struct obs_fader; +struct obs_volmeter; typedef struct obs_display obs_display_t; typedef struct obs_view obs_view_t; @@ -59,6 +60,7 @@ typedef struct obs_encoder obs_encoder_t; typedef struct obs_service obs_service_t; typedef struct obs_module obs_module_t; typedef struct obs_fader obs_fader_t; +typedef struct obs_volmeter obs_volmeter_t; #include "obs-source.h" #include "obs-encoder.h"