libobs: Add audio control functions
This adds a new library of audio control functions mainly for the use in GUIS. For now it includes an implementation of a software fader that can be attached to sources in order to easily control the volume. The fader can translate between fader-position, volume in dB and multiplier with a configurable mapping function. Currently only a cubic mapping (mul = fader_pos ^ 3) is included, but different mappings can easily be added. Due to libobs saving/restoring the source volume from the multiplier, the volume levels for existing source will stay the same, and live changing of the mapping will work without changing the source volume.
This commit is contained in:
parent
e994d6d498
commit
8dcbd77bf2
@ -190,6 +190,7 @@ set(libobs_util_HEADERS
|
||||
|
||||
set(libobs_libobs_SOURCES
|
||||
${libobs_PLATFORM_SOURCES}
|
||||
obs-audio-controls.c
|
||||
obs-avc.c
|
||||
obs-encoder.c
|
||||
obs-service.c
|
||||
@ -204,6 +205,7 @@ set(libobs_libobs_SOURCES
|
||||
obs-scene.c
|
||||
obs-video.c)
|
||||
set(libobs_libobs_HEADERS
|
||||
obs-audio-controls.h
|
||||
obs-defs.h
|
||||
obs-avc.h
|
||||
obs-encoder.h
|
||||
|
300
libobs/obs-audio-controls.c
Normal file
300
libobs/obs-audio-controls.c
Normal file
@ -0,0 +1,300 @@
|
||||
/*
|
||||
Copyright (C) 2014 by Leonhard Oelke <leonhard@in-verted.de>
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <math.h>
|
||||
|
||||
#include "util/threading.h"
|
||||
#include "util/bmem.h"
|
||||
#include "obs.h"
|
||||
#include "obs-internal.h"
|
||||
|
||||
#include "obs-audio-controls.h"
|
||||
|
||||
typedef float (*obs_fader_conversion_t)(const float val);
|
||||
|
||||
struct obs_fader {
|
||||
pthread_mutex_t mutex;
|
||||
signal_handler_t *signals;
|
||||
obs_fader_conversion_t def_to_db;
|
||||
obs_fader_conversion_t db_to_def;
|
||||
obs_source_t *source;
|
||||
enum obs_fader_type type;
|
||||
float max_db;
|
||||
float min_db;
|
||||
float cur_db;
|
||||
bool ignore_next_signal;
|
||||
};
|
||||
|
||||
static const char *fader_signals[] = {
|
||||
"void volume_changed(ptr fader, float db)",
|
||||
NULL
|
||||
};
|
||||
|
||||
static inline float mul_to_db(const float mul)
|
||||
{
|
||||
return (mul == 0.0f) ? -INFINITY : 20.0f * log10f(mul);
|
||||
}
|
||||
|
||||
static inline float db_to_mul(const float db)
|
||||
{
|
||||
return (db == -INFINITY) ? 0.0f : powf(10.0f, db / 20.0f);
|
||||
}
|
||||
|
||||
static float cubic_def_to_db(const float def)
|
||||
{
|
||||
if (def == 1.0f)
|
||||
return 0.0f;
|
||||
else if (def <= 0.0f)
|
||||
return -INFINITY;
|
||||
|
||||
return mul_to_db(def * def * def);
|
||||
}
|
||||
|
||||
static float cubic_db_to_def(const float db)
|
||||
{
|
||||
if (db == 0.0f)
|
||||
return 1.0f;
|
||||
else if (db == -INFINITY)
|
||||
return 0.0f;
|
||||
|
||||
return cbrtf(db_to_mul(db));
|
||||
}
|
||||
|
||||
static void signal_volume_changed(signal_handler_t *sh,
|
||||
struct obs_fader *fader, const float db)
|
||||
{
|
||||
struct calldata data;
|
||||
|
||||
calldata_init(&data);
|
||||
|
||||
calldata_set_ptr (&data, "fader", fader);
|
||||
calldata_set_float(&data, "db", db);
|
||||
|
||||
signal_handler_signal(sh, "volume_changed", &data);
|
||||
|
||||
calldata_free(&data);
|
||||
}
|
||||
|
||||
static void fader_source_volume_changed(void *vptr, calldata_t *calldata)
|
||||
{
|
||||
struct obs_fader *fader = (struct obs_fader *) vptr;
|
||||
|
||||
pthread_mutex_lock(&fader->mutex);
|
||||
|
||||
if (fader->ignore_next_signal) {
|
||||
fader->ignore_next_signal = false;
|
||||
pthread_mutex_unlock(&fader->mutex);
|
||||
return;
|
||||
}
|
||||
|
||||
signal_handler_t *sh = fader->signals;
|
||||
const float mul = calldata_float(calldata, "volume");
|
||||
const float db = mul_to_db(mul);
|
||||
fader->cur_db = db;
|
||||
|
||||
pthread_mutex_unlock(&fader->mutex);
|
||||
|
||||
signal_volume_changed(sh, fader, db);
|
||||
}
|
||||
|
||||
static void fader_source_destroyed(void *vptr, calldata_t *calldata)
|
||||
{
|
||||
UNUSED_PARAMETER(calldata);
|
||||
struct obs_fader *fader = (struct obs_fader *) vptr;
|
||||
|
||||
obs_fader_detach_source(fader);
|
||||
}
|
||||
|
||||
obs_fader_t *obs_fader_create(enum obs_fader_type type)
|
||||
{
|
||||
struct obs_fader *fader = bzalloc(sizeof(struct obs_fader));
|
||||
if (!fader)
|
||||
return NULL;
|
||||
|
||||
pthread_mutex_init_value(&fader->mutex);
|
||||
if (pthread_mutex_init(&fader->mutex, NULL) != 0)
|
||||
goto fail;
|
||||
fader->signals = signal_handler_create();
|
||||
if (!fader->signals)
|
||||
goto fail;
|
||||
if (!signal_handler_add_array(fader->signals, fader_signals))
|
||||
goto fail;
|
||||
|
||||
switch(type) {
|
||||
case OBS_FADER_CUBIC:
|
||||
fader->def_to_db = cubic_def_to_db;
|
||||
fader->db_to_def = cubic_db_to_def;
|
||||
fader->max_db = 0.0f;
|
||||
fader->min_db = -INFINITY;
|
||||
break;
|
||||
default:
|
||||
goto fail;
|
||||
break;
|
||||
}
|
||||
fader->type = type;
|
||||
|
||||
return fader;
|
||||
fail:
|
||||
obs_fader_destroy(fader);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void obs_fader_destroy(obs_fader_t *fader)
|
||||
{
|
||||
if (!fader)
|
||||
return;
|
||||
|
||||
obs_fader_detach_source(fader);
|
||||
signal_handler_destroy(fader->signals);
|
||||
pthread_mutex_destroy(&fader->mutex);
|
||||
|
||||
bfree(fader);
|
||||
}
|
||||
|
||||
bool obs_fader_set_db(obs_fader_t *fader, const float db)
|
||||
{
|
||||
if (!fader)
|
||||
return false;
|
||||
|
||||
pthread_mutex_lock(&fader->mutex);
|
||||
|
||||
bool clamped = false;
|
||||
fader->cur_db = db;
|
||||
|
||||
if (fader->cur_db > fader->max_db) {
|
||||
fader->cur_db = fader->max_db;
|
||||
clamped = true;
|
||||
}
|
||||
if (fader->cur_db < fader->min_db) {
|
||||
fader->cur_db = -INFINITY;
|
||||
clamped = true;
|
||||
}
|
||||
|
||||
fader->ignore_next_signal = true;
|
||||
obs_source_t *src = fader->source;
|
||||
const float mul = db_to_mul(fader->cur_db);
|
||||
|
||||
pthread_mutex_unlock(&fader->mutex);
|
||||
|
||||
if (src)
|
||||
obs_source_set_volume(src, mul);
|
||||
|
||||
return !clamped;
|
||||
}
|
||||
|
||||
float obs_fader_get_db(obs_fader_t *fader)
|
||||
{
|
||||
if (!fader)
|
||||
return 0.0f;
|
||||
|
||||
pthread_mutex_lock(&fader->mutex);
|
||||
const float db = fader->cur_db;
|
||||
pthread_mutex_unlock(&fader->mutex);
|
||||
|
||||
return db;
|
||||
}
|
||||
|
||||
bool obs_fader_set_deflection(obs_fader_t *fader, const float def)
|
||||
{
|
||||
if (!fader)
|
||||
return false;
|
||||
|
||||
return obs_fader_set_db(fader, fader->def_to_db(def));
|
||||
}
|
||||
|
||||
float obs_fader_get_deflection(obs_fader_t *fader)
|
||||
{
|
||||
if (!fader)
|
||||
return 0.0f;
|
||||
|
||||
pthread_mutex_lock(&fader->mutex);
|
||||
const float def = fader->db_to_def(fader->cur_db);
|
||||
pthread_mutex_unlock(&fader->mutex);
|
||||
|
||||
return def;
|
||||
}
|
||||
|
||||
bool obs_fader_set_mul(obs_fader_t *fader, const float mul)
|
||||
{
|
||||
if (!fader)
|
||||
return false;
|
||||
|
||||
return obs_fader_set_db(fader, mul_to_db(mul));
|
||||
}
|
||||
|
||||
float obs_fader_get_mul(obs_fader_t *fader)
|
||||
{
|
||||
if (!fader)
|
||||
return 0.0f;
|
||||
|
||||
pthread_mutex_lock(&fader->mutex);
|
||||
const float mul = db_to_mul(fader->cur_db);
|
||||
pthread_mutex_unlock(&fader->mutex);
|
||||
|
||||
return mul;
|
||||
}
|
||||
|
||||
bool obs_fader_attach_source(obs_fader_t *fader, obs_source_t *source)
|
||||
{
|
||||
if (!fader || !source)
|
||||
return false;
|
||||
|
||||
obs_fader_detach_source(fader);
|
||||
|
||||
pthread_mutex_lock(&fader->mutex);
|
||||
|
||||
signal_handler_t *sh = obs_source_get_signal_handler(source);
|
||||
signal_handler_connect(sh, "volume",
|
||||
fader_source_volume_changed, fader);
|
||||
signal_handler_connect(sh, "destroy",
|
||||
fader_source_destroyed, fader);
|
||||
|
||||
fader->source = source;
|
||||
fader->cur_db = mul_to_db(obs_source_get_volume(source));
|
||||
|
||||
pthread_mutex_unlock(&fader->mutex);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void obs_fader_detach_source(obs_fader_t *fader)
|
||||
{
|
||||
if (!fader)
|
||||
return;
|
||||
|
||||
pthread_mutex_lock(&fader->mutex);
|
||||
|
||||
if (!fader->source)
|
||||
goto exit;
|
||||
|
||||
signal_handler_t *sh = obs_source_get_signal_handler(fader->source);
|
||||
signal_handler_disconnect(sh, "volume",
|
||||
fader_source_volume_changed, fader);
|
||||
signal_handler_disconnect(sh, "destroy",
|
||||
fader_source_destroyed, fader);
|
||||
|
||||
fader->source = NULL;
|
||||
|
||||
exit:
|
||||
pthread_mutex_unlock(&fader->mutex);
|
||||
}
|
||||
|
||||
signal_handler_t *obs_fader_get_signal_handler(obs_fader_t *fader)
|
||||
{
|
||||
return (fader) ? fader->signals : NULL;
|
||||
}
|
||||
|
142
libobs/obs-audio-controls.h
Normal file
142
libobs/obs-audio-controls.h
Normal file
@ -0,0 +1,142 @@
|
||||
/*
|
||||
Copyright (C) 2014 by Leonhard Oelke <leonhard@in-verted.de>
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "obs.h"
|
||||
|
||||
/**
|
||||
* @file
|
||||
* @brief header for audio controls
|
||||
*
|
||||
* @brief Audio controls for use in GUIs
|
||||
*/
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Fader types
|
||||
*/
|
||||
enum obs_fader_type {
|
||||
/**
|
||||
* @brief A simple cubic fader for controlling audio levels
|
||||
*
|
||||
* This is a very common type of software fader since it yields good
|
||||
* results while being quite performant.
|
||||
* The input value is mapped to mul values with the simple formula x^3.
|
||||
*/
|
||||
OBS_FADER_CUBIC
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Create a fader
|
||||
* @param type the type of the fader
|
||||
* @return pointer to the fader object
|
||||
*
|
||||
* A fader object is used to map input values from a gui element to dB and
|
||||
* subsequently multiplier values used by libobs to mix audio.
|
||||
* The current "position" of the fader is internally stored as dB value.
|
||||
*/
|
||||
EXPORT obs_fader_t *obs_fader_create(enum obs_fader_type type);
|
||||
|
||||
/**
|
||||
* @brief Destroy a fader
|
||||
* @param fader pointer to the fader object
|
||||
*
|
||||
* Destroy the fader and free all related data
|
||||
*/
|
||||
EXPORT void obs_fader_destroy(obs_fader_t *fader);
|
||||
|
||||
/**
|
||||
* @brief Set the fader dB value
|
||||
* @param fader pointer to the fader object
|
||||
* @param db new dB value
|
||||
* @return true if value was set without clamping
|
||||
*/
|
||||
EXPORT bool obs_fader_set_db(obs_fader_t *fader, const float db);
|
||||
|
||||
/**
|
||||
* @brief Get the current fader dB value
|
||||
* @param fader pointer to the fader object
|
||||
* @return current fader dB value
|
||||
*/
|
||||
EXPORT float obs_fader_get_db(obs_fader_t *fader);
|
||||
|
||||
/**
|
||||
* @brief Set the fader value from deflection
|
||||
* @param fader pointer to the fader object
|
||||
* @param def new deflection
|
||||
* @return true if value was set without clamping
|
||||
*
|
||||
* This sets the new fader value from the supplied deflection, in case the
|
||||
* resulting value was clamped due to limits this function will return false.
|
||||
* The deflection is typically in the range [0.0, 1.0] but may be higher in
|
||||
* order to provide some amplification. In order for this to work the high dB
|
||||
* limit has to be set.
|
||||
*/
|
||||
EXPORT bool obs_fader_set_deflection(obs_fader_t *fader, const float def);
|
||||
|
||||
/**
|
||||
* @brief Get the current fader deflection
|
||||
* @param fader pointer to the fader object
|
||||
* @return current fader deflection
|
||||
*/
|
||||
EXPORT float obs_fader_get_deflection(obs_fader_t *fader);
|
||||
|
||||
/**
|
||||
* @brief Set the fader value from multiplier
|
||||
* @param fader pointer to the fader object
|
||||
* @return true if the value was set without clamping
|
||||
*/
|
||||
EXPORT bool obs_fader_set_mul(obs_fader_t *fader, const float mul);
|
||||
|
||||
/**
|
||||
* @brief Get the current fader multiplier value
|
||||
* @param fader pointer to the fader object
|
||||
* @return current fader multiplier
|
||||
*/
|
||||
EXPORT float obs_fader_get_mul(obs_fader_t *fader);
|
||||
|
||||
/**
|
||||
* @brief Attach the fader to a source
|
||||
* @param fader pointer to the fader object
|
||||
* @param source pointer to the source object
|
||||
* @return true on success
|
||||
*
|
||||
* When the fader is attached to a source it will automatically sync it's state
|
||||
* to the volume of the source.
|
||||
*/
|
||||
EXPORT bool obs_fader_attach_source(obs_fader_t *fader, obs_source_t *source);
|
||||
|
||||
/**
|
||||
* @brief Detach the fader from the currently attached source
|
||||
* @param fader pointer to the fader object
|
||||
*/
|
||||
EXPORT void obs_fader_detach_source(obs_fader_t *fader);
|
||||
|
||||
/**
|
||||
* @brief Get signal handler for the fader
|
||||
* @param fader pointer to the fader object
|
||||
* @return signal handler
|
||||
*/
|
||||
EXPORT signal_handler_t *obs_fader_get_signal_handler(obs_fader_t *fader);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
@ -47,6 +47,7 @@ struct obs_output;
|
||||
struct obs_encoder;
|
||||
struct obs_service;
|
||||
struct obs_module;
|
||||
struct obs_fader;
|
||||
|
||||
typedef struct obs_display obs_display_t;
|
||||
typedef struct obs_view obs_view_t;
|
||||
@ -57,11 +58,13 @@ typedef struct obs_output obs_output_t;
|
||||
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;
|
||||
|
||||
#include "obs-source.h"
|
||||
#include "obs-encoder.h"
|
||||
#include "obs-output.h"
|
||||
#include "obs-service.h"
|
||||
#include "obs-audio-controls.h"
|
||||
|
||||
/**
|
||||
* @file
|
||||
|
Loading…
x
Reference in New Issue
Block a user