提交 fe6622c6 编写于 作者: J Jim

Merge pull request #306 from fryshorts/volume-meter-improvements

Volume meter improvements
......@@ -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);
......@@ -81,6 +97,94 @@ static float cubic_db_to_def(const float db)
return cbrtf(db_to_mul(db));
}
static float iec_def_to_db(const float def)
{
if (def == 1.0f)
return 0.0f;
else if (def <= 0.0f)
return -INFINITY;
float db;
if (def >= 0.75f)
db = (def - 1.0f) / 0.25f * 9.0f;
else if (def >= 0.5f)
db = (def - 0.75f) / 0.25f * 11.0f - 9.0f;
else if (def >= 0.3f)
db = (def - 0.5f) / 0.2f * 10.0f - 20.0f;
else if (def >= 0.15f)
db = (def - 0.3f) / 0.15f * 10.0f - 30.0f;
else if (def >= 0.075f)
db = (def - 0.15f) / 0.075f * 10.0f - 40.0f;
else if (def >= 0.025f)
db = (def - 0.075f) / 0.05f * 10.0f - 50.0f;
else if (def >= 0.001f)
db = (def - 0.025f) / 0.025f * 90.0f - 60.0f;
else
db = -INFINITY;
return db;
}
static float iec_db_to_def(const float db)
{
if (db == 0.0f)
return 1.0f;
else if (db == -INFINITY)
return 0.0f;
float def;
if (db >= -9.0f)
def = (db + 9.0f) / 9.0f * 0.25f + 0.75f;
else if (db >= -20.0f)
def = (db + 20.0f) / 11.0f * 0.25f + 0.5f;
else if (db >= -30.0f)
def = (db + 30.0f) / 10.0f * 0.2f + 0.3f;
else if (db >= -40.0f)
def = (db + 40.0f) / 10.0f * 0.15f + 0.15f;
else if (db >= -50.0f)
def = (db + 50.0f) / 10.0f * 0.075f + 0.075f;
else if (db >= -60.0f)
def = (db + 60.0f) / 10.0f * 0.05f + 0.025f;
else if (db >= -114.0f)
def = (db + 150.0f) / 90.0f * 0.025f;
else
def = 0.0f;
return def;
}
#define LOG_OFFSET_DB 6.0f
#define LOG_RANGE_DB 96.0f
/* equals -log10f(LOG_OFFSET_DB) */
#define LOG_OFFSET_VAL -0.77815125038364363f
/* equals -log10f(-LOG_RANGE_DB + LOG_OFFSET_DB) */
#define LOG_RANGE_VAL -2.00860017176191756f
static float log_def_to_db(const float def)
{
if (def >= 1.0f)
return 0.0f;
else if (def <= 0.0f)
return -INFINITY;
return -(LOG_RANGE_DB + LOG_OFFSET_DB) * powf(
(LOG_RANGE_DB + LOG_OFFSET_DB) / LOG_OFFSET_DB, -def)
+ LOG_OFFSET_DB;
}
static float log_db_to_def(const float db)
{
if (db >= 0.0f)
return 1.0f;
else if (db <= -96.0f)
return 0.0f;
return (-log10f(-db + LOG_OFFSET_DB) - LOG_RANGE_VAL)
/ (LOG_OFFSET_VAL - LOG_RANGE_VAL);
}
static void signal_volume_changed(signal_handler_t *sh,
struct obs_fader *fader, const float db)
{
......@@ -96,6 +200,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 +240,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 +260,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));
......@@ -148,6 +313,18 @@ obs_fader_t *obs_fader_create(enum obs_fader_type type)
fader->max_db = 0.0f;
fader->min_db = -INFINITY;
break;
case OBS_FADER_IEC:
fader->def_to_db = iec_def_to_db;
fader->db_to_def = iec_db_to_def;
fader->max_db = 0.0f;
fader->min_db = -INFINITY;
break;
case OBS_FADER_LOG:
fader->def_to_db = log_def_to_db;
fader->db_to_def = log_db_to_def;
fader->max_db = 0.0f;
fader->min_db = -96.0f;
break;
default:
goto fail;
break;
......@@ -309,3 +486,114 @@ 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;
case OBS_FADER_IEC:
volmeter->pos_to_db = iec_def_to_db;
volmeter->db_to_pos = iec_db_to_def;
break;
case OBS_FADER_LOG:
volmeter->pos_to_db = log_def_to_db;
volmeter->db_to_pos = log_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;
}
......@@ -41,7 +41,31 @@ enum obs_fader_type {
* results while being quite performant.
* The input value is mapped to mul values with the simple formula x^3.
*/
OBS_FADER_CUBIC
OBS_FADER_CUBIC,
/**
* @brief A fader compliant to IEC 60-268-18
*
* This type of fader has several segments with different slopes that
* map deflection linearly to dB values. The segments are defined as
* in the following table:
*
@code
Deflection | Volume
------------------------------------------
[ 100 %, 75 % ] | [ 0 dB, -9 dB ]
[ 75 %, 50 % ] | [ -9 dB, -20 dB ]
[ 50 %, 30 % ] | [ -20 dB, -30 dB ]
[ 30 %, 15 % ] | [ -30 dB, -40 dB ]
[ 15 %, 7.5 % ] | [ -40 dB, -50 dB ]
[ 7.5 %, 2.5 % ] | [ -50 dB, -60 dB ]
[ 2.5 %, 0 % ] | [ -60 dB, -inf dB ]
@endcode
*/
OBS_FADER_IEC,
/**
* @brief Logarithmic fader
*/
OBS_FADER_LOG
};
/**
......@@ -137,6 +161,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
......@@ -627,15 +627,6 @@ static inline void handle_ts_jump(obs_source_t *source, uint64_t expected,
reset_audio_timing(source, ts, os_time);
}
#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 volume)
{
......@@ -665,8 +656,8 @@ static void calc_volume_levels(struct obs_source *source, float *array,
UNUSED_PARAMETER(volume);
rms_val = to_db(sqrtf(sum_val / (float)count));
max_val = to_db(sqrtf(max_val));
rms_val = sqrtf(sum_val / (float)count);
max_val = sqrtf(max_val);
if (max_val > source->vol_max)
source->vol_max = max_val;
......
......@@ -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"
......
......@@ -12,31 +12,8 @@
using namespace std;
#define VOL_MIN -96.0f
#define VOL_MAX 0.0f
/*
VOL_MIN_LOG = DBToLog(VOL_MIN)
VOL_MAX_LOG = DBToLog(VOL_MAX)
... just in case someone wants to use a smaller scale
*/
#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)
{
Q_UNUSED(calldata);
......@@ -73,11 +50,8 @@ void VolControl::VolumeLevel(float mag, float peak, float peakHold)
/* only update after a certain amount of time */
if ((curMeterTime - lastMeterTime) > UPDATE_INTERVAL_MS) {
float vol = (float)slider->value() * 0.01f;
lastMeterTime = curMeterTime;
volMeter->setLevels(DBToLinear(mag) * vol,
DBToLinear(peak) * vol,
DBToLinear(peakHold) * vol);
volMeter->setLevels(mag, peak, peakHold);
}
}
......@@ -103,7 +77,8 @@ VolControl::VolControl(OBSSource source_)
lastMeterTime (0),
levelTotal (0.0f),
levelCount (0.0f),
obs_fader (obs_fader_create(OBS_FADER_CUBIC))
obs_fader (obs_fader_create(OBS_FADER_CUBIC)),
obs_volmeter (obs_volmeter_create(OBS_FADER_LOG))
{
QVBoxLayout *mainLayout = new QVBoxLayout();
QHBoxLayout *textLayout = new QHBoxLayout();
......@@ -141,13 +116,15 @@ VolControl::VolControl(OBSSource source_)
signal_handler_connect(obs_fader_get_signal_handler(obs_fader),
"volume_changed", OBSVolumeChanged, this);
signal_handler_connect(obs_source_get_signal_handler(source),
"volume_level", OBSVolumeLevel, this);
signal_handler_connect(obs_volmeter_get_signal_handler(obs_volmeter),
"levels_updated", OBSVolumeLevel, this);
QWidget::connect(slider, SIGNAL(valueChanged(int)),
this, SLOT(SliderChanged(int)));
obs_fader_attach_source(obs_fader, source);
obs_volmeter_attach_source(obs_volmeter, source);
/* Call volume changed once to init the slider position and label */
VolumeChanged();
}
......@@ -157,10 +134,11 @@ VolControl::~VolControl()
signal_handler_disconnect(obs_fader_get_signal_handler(obs_fader),
"volume_changed", OBSVolumeChanged, this);
signal_handler_disconnect(obs_source_get_signal_handler(source),
"volume_level", OBSVolumeLevel, this);
signal_handler_disconnect(obs_volmeter_get_signal_handler(obs_volmeter),
"levels_updated", OBSVolumeLevel, this);
obs_fader_destroy(obs_fader);
obs_volmeter_destroy(obs_volmeter);
}
VolumeMeter::VolumeMeter(QWidget *parent)
......
......@@ -36,6 +36,7 @@ private:
float levelTotal;
float levelCount;
obs_fader_t *obs_fader;
obs_volmeter_t *obs_volmeter;
static void OBSVolumeChanged(void *param, calldata_t *calldata);
static void OBSVolumeLevel(void *data, calldata_t *calldata);
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册