提交 5cd64ce7 编写于 作者: J jp9000

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.
上级 d891e977
......@@ -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,
......
......@@ -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)
{
......
......@@ -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)",
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册