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.
master
fryshorts 2014-11-19 00:50:32 +01:00
parent e994d6d498
commit 8dcbd77bf2
4 changed files with 447 additions and 0 deletions

View File

@ -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
View 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
View 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

View File

@ -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