libobs: Add level calculations to volmeter

Use the new audio_data signal from sources to calculate the volume
levels in the volume meter.
This commit is contained in:
fryshorts 2014-12-14 17:52:20 +01:00
parent 63399bbfd8
commit 6e0c1533fc

View File

@ -60,6 +60,15 @@ struct obs_volmeter {
unsigned int update_frames;
unsigned int peakhold_ms;
unsigned int peakhold_frames;
unsigned int peakhold_count;
unsigned int ival_frames;
float ival_sum;
float ival_max;
float vol_peak;
float vol_mag;
float vol_max;
};
static const char *fader_signals[] = {
@ -297,6 +306,106 @@ static void volmeter_source_destroyed(void *vptr, calldata_t *calldata)
obs_volmeter_detach_source(volmeter);
}
static void volmeter_sum_and_max(float *data, size_t frames,
float *sum, float *max)
{
float s = *sum;
float m = *max;
for (float *c = data; c < data + frames; ++c) {
const float pow = *c * *c;
s += pow;
m = (m > pow) ? m : pow;
}
*sum = s;
*max = m;
}
/**
* @todo The IIR low pass filter has a different behavior depending on the
* update interval and sample rate, it should be replaced with something
* that is independent from both.
*/
static void volmeter_calc_ival_levels(obs_volmeter_t *volmeter)
{
const float alpha = 0.15f;
const float frames = (float) volmeter->ival_frames;
const float samples = frames * (float) volmeter->channels;
const float ival_max = sqrtf(volmeter->ival_max);
const float ival_rms = sqrtf(volmeter->ival_sum / samples);
if (ival_max > volmeter->vol_max) {
volmeter->vol_max = ival_max;
} else {
volmeter->vol_max = alpha * volmeter->vol_max +
(1.0f - alpha) * ival_max;
}
if (volmeter->vol_max > volmeter->vol_peak ||
volmeter->peakhold_count > volmeter->peakhold_frames) {
volmeter->vol_peak = volmeter->vol_max;
volmeter->peakhold_count = 0;
} else {
volmeter->peakhold_count += frames;
}
volmeter->vol_mag = alpha * ival_rms +
volmeter->vol_mag * (1.0f - alpha);
/* reset interval data */
volmeter->ival_frames = 0;
volmeter->ival_sum = 0.0f;
volmeter->ival_max = 0.0f;
}
static bool volmeter_process_audio_data(obs_volmeter_t *volmeter,
struct audio_data *data)
{
bool updated = false;
size_t frames = 0;
size_t samples = 0;
size_t left = data->frames;
float *adata = (float *) data->data[0];
while (left) {
frames = (volmeter->ival_frames + left >
volmeter->update_frames)
? volmeter->update_frames - volmeter->ival_frames
: left;
samples = frames * volmeter->channels;
volmeter_sum_and_max(adata, samples, &volmeter->ival_sum,
&volmeter->ival_max);
volmeter->ival_frames += frames;
left -= frames;
adata += samples;
/* break if we did not reach the end of the interval */
if (volmeter->ival_frames != volmeter->update_frames)
break;
volmeter_calc_ival_levels(volmeter);
updated = true;
}
return updated;
}
static void volmeter_source_data_received(void *vptr, calldata_t *calldata)
{
struct obs_volmeter *volmeter = (struct obs_volmeter *) vptr;
bool updated = false;
pthread_mutex_lock(&volmeter->mutex);
struct audio_data *data = calldata_ptr(calldata, "data");
updated = volmeter_process_audio_data(volmeter, data);
pthread_mutex_unlock(&volmeter->mutex);
}
static void volmeter_update_audio_settings(obs_volmeter_t *volmeter)
{
audio_t *audio = obs_get_audio();
@ -574,6 +683,8 @@ bool obs_volmeter_attach_source(obs_volmeter_t *volmeter, obs_source_t *source)
volmeter_source_volume_changed, volmeter);
signal_handler_connect(sh, "volume_level",
volmeter_source_volume_levels, volmeter);
signal_handler_connect(sh, "audio_data",
volmeter_source_data_received, volmeter);
signal_handler_connect(sh, "destroy",
volmeter_source_destroyed, volmeter);
@ -602,6 +713,8 @@ void obs_volmeter_detach_source(obs_volmeter_t *volmeter)
volmeter_source_volume_changed, volmeter);
signal_handler_disconnect(sh, "volume_level",
volmeter_source_volume_levels, volmeter);
signal_handler_disconnect(sh, "audio_data",
volmeter_source_data_received, volmeter);
signal_handler_disconnect(sh, "destroy",
volmeter_source_destroyed, volmeter);