Added simple volume meter for reference of input levels.

This commit is contained in:
Danni 2014-05-20 09:26:18 -05:00
parent 1e525c4713
commit bc542a3e75
6 changed files with 114 additions and 25 deletions

View file

@ -713,8 +713,9 @@ uint32_t audio_output_samplerate(audio_t audio)
/* TODO: Optimization of volume multiplication functions */ /* TODO: Optimization of volume multiplication functions */
static inline void mul_vol_u8bit(void *array, float volume, size_t total_num) static inline int mul_vol_u8bit(void *array, float volume, size_t total_num)
{ {
int maxVol = 0;
uint8_t *vals = array; uint8_t *vals = array;
int32_t vol = (int32_t)(volume * 127.0f); int32_t vol = (int32_t)(volume * 127.0f);
@ -722,18 +723,23 @@ static inline void mul_vol_u8bit(void *array, float volume, size_t total_num)
int32_t val = (int32_t)vals[i] - 128; int32_t val = (int32_t)vals[i] - 128;
int32_t output = val * vol / 127; int32_t output = val * vol / 127;
vals[i] = (uint8_t)(CLAMP(output, MIN_S8, MAX_S8) + 128); vals[i] = (uint8_t)(CLAMP(output, MIN_S8, MAX_S8) + 128);
maxVol = max(maxVol, abs(vals[i]));
} }
return maxVol * (10000 / MAX_S8);
} }
static inline void mul_vol_16bit(void *array, float volume, size_t total_num) static inline int mul_vol_16bit(void *array, float volume, size_t total_num)
{ {
int maxVol = 0;
uint16_t *vals = array; uint16_t *vals = array;
int64_t vol = (int64_t)(volume * 32767.0f); int64_t vol = (int64_t)(volume * 32767.0f);
for (size_t i = 0; i < total_num; i++) { for (size_t i = 0; i < total_num; i++) {
int64_t output = (int64_t)vals[i] * vol / 32767; int64_t output = (int64_t)vals[i] * vol / 32767;
vals[i] = (int32_t)CLAMP(output, MIN_S16, MAX_S16); vals[i] = (int32_t)CLAMP(output, MIN_S16, MAX_S16);
maxVol = max(maxVol, abs(vals[i]));
} }
return maxVol * (10000 / MAX_S16);
} }
static inline float conv_24bit_to_float(uint8_t *vals) static inline float conv_24bit_to_float(uint8_t *vals)
@ -756,19 +762,23 @@ static inline void conv_float_to_24bit(float fval, uint8_t *vals)
vals[2] = (val >> 16) & 0xFF; vals[2] = (val >> 16) & 0xFF;
} }
static inline void mul_vol_24bit(void *array, float volume, size_t total_num) static inline int mul_vol_24bit(void *array, float volume, size_t total_num)
{ {
float maxVol = 0.f;
uint8_t *vals = array; uint8_t *vals = array;
for (size_t i = 0; i < total_num; i++) { for (size_t i = 0; i < total_num; i++) {
float val = conv_24bit_to_float(vals) * volume; float val = conv_24bit_to_float(vals) * volume;
conv_float_to_24bit(CLAMP(val, -1.0f, 1.0f), vals); conv_float_to_24bit(CLAMP(val, -1.0f, 1.0f), vals);
vals += 3; vals += 3;
maxVol = max(maxVol, (float)fabs(val));
} }
return (int) (maxVol * 10000.f);
} }
static inline void mul_vol_32bit(void *array, float volume, size_t total_num) static inline int mul_vol_32bit(void *array, float volume, size_t total_num)
{ {
int maxVol = 0;
int32_t *vals = array; int32_t *vals = array;
double dvol = (double)volume; double dvol = (double)volume;
@ -776,20 +786,33 @@ static inline void mul_vol_32bit(void *array, float volume, size_t total_num)
double val = (double)vals[i] / 2147483647.0; double val = (double)vals[i] / 2147483647.0;
double output = val * dvol; double output = val * dvol;
vals[i] = (int32_t)(CLAMP(output, -1.0, 1.0) * 2147483647.0); vals[i] = (int32_t)(CLAMP(output, -1.0, 1.0) * 2147483647.0);
maxVol = max(maxVol, abs(vals[i]));
} }
return maxVol * (10000 / MAX_S32);
} }
static inline void mul_vol_float(void *array, float volume, size_t total_num) static inline int mul_vol_float(void *array, float volume, size_t total_num)
{ {
float maxVol = 0;
float *vals = array; float *vals = array;
for (size_t i = 0; i < total_num; i++) for (size_t i = 0; i < total_num; i++) {
vals[i] *= volume; vals[i] *= volume;
maxVol = max(maxVol, (float)fabs(vals[i]));
}
return (int)(maxVol * 10000.f);
} }
static void audio_line_place_data_pos(struct audio_line *line, // [Danni] changed to int for volume feedback. Seems like the most logical
// place to calculate this to avoid unnessisary iterations.
// scaled to max of 10000.
static int audio_line_place_data_pos(struct audio_line *line,
const struct audio_data *data, size_t position) const struct audio_data *data, size_t position)
{ {
int maxVol = 0;
bool planar = line->audio->planes > 1; bool planar = line->audio->planes > 1;
size_t total_num = data->frames * (planar ? 1 : line->audio->channels); size_t total_num = data->frames * (planar ? 1 : line->audio->channels);
size_t total_size = data->frames * line->audio->block_size; size_t total_size = data->frames * line->audio->block_size;
@ -803,19 +826,19 @@ static void audio_line_place_data_pos(struct audio_line *line,
switch (line->audio->info.format) { switch (line->audio->info.format) {
case AUDIO_FORMAT_U8BIT: case AUDIO_FORMAT_U8BIT:
case AUDIO_FORMAT_U8BIT_PLANAR: case AUDIO_FORMAT_U8BIT_PLANAR:
mul_vol_u8bit(array, data->volume, total_num); maxVol = mul_vol_u8bit(array, data->volume, total_num);
break; break;
case AUDIO_FORMAT_16BIT: case AUDIO_FORMAT_16BIT:
case AUDIO_FORMAT_16BIT_PLANAR: case AUDIO_FORMAT_16BIT_PLANAR:
mul_vol_16bit(array, data->volume, total_num); maxVol = mul_vol_16bit(array, data->volume, total_num);
break; break;
case AUDIO_FORMAT_32BIT: case AUDIO_FORMAT_32BIT:
case AUDIO_FORMAT_32BIT_PLANAR: case AUDIO_FORMAT_32BIT_PLANAR:
mul_vol_32bit(array, data->volume, total_num); maxVol = mul_vol_32bit(array, data->volume, total_num);
break; break;
case AUDIO_FORMAT_FLOAT: case AUDIO_FORMAT_FLOAT:
case AUDIO_FORMAT_FLOAT_PLANAR: case AUDIO_FORMAT_FLOAT_PLANAR:
mul_vol_float(array, data->volume, total_num); maxVol = mul_vol_float(array, data->volume, total_num);
break; break;
case AUDIO_FORMAT_UNKNOWN: case AUDIO_FORMAT_UNKNOWN:
blog(LOG_ERROR, "audio_line_place_data_pos: " blog(LOG_ERROR, "audio_line_place_data_pos: "
@ -826,9 +849,10 @@ static void audio_line_place_data_pos(struct audio_line *line,
circlebuf_place(&line->buffers[i], position, circlebuf_place(&line->buffers[i], position,
line->volume_buffers[i].array, total_size); line->volume_buffers[i].array, total_size);
} }
return maxVol;
} }
static void audio_line_place_data(struct audio_line *line, static int audio_line_place_data(struct audio_line *line,
const struct audio_data *data) const struct audio_data *data)
{ {
size_t pos = ts_diff_bytes(line->audio, data->timestamp, size_t pos = ts_diff_bytes(line->audio, data->timestamp,
@ -842,25 +866,26 @@ static void audio_line_place_data(struct audio_line *line,
line->buffers[0].size); line->buffers[0].size);
#endif #endif
audio_line_place_data_pos(line, data, pos); return audio_line_place_data_pos(line, data, pos);
} }
void audio_line_output(audio_line_t line, const struct audio_data *data) int audio_line_output(audio_line_t line, const struct audio_data *data)
{ {
/* TODO: prevent insertation of data too far away from expected /* TODO: prevent insertation of data too far away from expected
* audio timing */ * audio timing */
if (!line || !data) return; if (!line || !data) return 0;
int maxVol = 0;
pthread_mutex_lock(&line->mutex); pthread_mutex_lock(&line->mutex);
if (!line->buffers[0].size) { if (!line->buffers[0].size) {
line->base_timestamp = data->timestamp - line->base_timestamp = data->timestamp -
line->audio->info.buffer_ms * 1000000; line->audio->info.buffer_ms * 1000000;
audio_line_place_data(line, data); maxVol = audio_line_place_data(line, data);
} else if (line->base_timestamp <= data->timestamp) { } else if (line->base_timestamp <= data->timestamp) {
audio_line_place_data(line, data); maxVol = audio_line_place_data(line, data);
} else { } else {
blog(LOG_DEBUG, "Bad timestamp for audio line '%s', " blog(LOG_DEBUG, "Bad timestamp for audio line '%s', "
@ -872,4 +897,5 @@ void audio_line_output(audio_line_t line, const struct audio_data *data)
} }
pthread_mutex_unlock(&line->mutex); pthread_mutex_unlock(&line->mutex);
return maxVol;
} }

View file

@ -190,7 +190,7 @@ EXPORT const struct audio_output_info *audio_output_getinfo(audio_t audio);
EXPORT audio_line_t audio_output_createline(audio_t audio, const char *name); EXPORT audio_line_t audio_output_createline(audio_t audio, const char *name);
EXPORT void audio_line_destroy(audio_line_t line); EXPORT void audio_line_destroy(audio_line_t line);
EXPORT void audio_line_output(audio_line_t line, const struct audio_data *data); EXPORT int audio_line_output(audio_line_t line, const struct audio_data *data);
#ifdef __cplusplus #ifdef __cplusplus

View file

@ -19,6 +19,7 @@
#include "media-io/format-conversion.h" #include "media-io/format-conversion.h"
#include "media-io/video-frame.h" #include "media-io/video-frame.h"
#include "media-io/audio-io.h"
#include "util/threading.h" #include "util/threading.h"
#include "util/platform.h" #include "util/platform.h"
#include "callback/calldata.h" #include "callback/calldata.h"
@ -28,6 +29,8 @@
#include "obs.h" #include "obs.h"
#include "obs-internal.h" #include "obs-internal.h"
static inline bool source_valid(struct obs_source *source) static inline bool source_valid(struct obs_source *source)
{ {
return source && source->context.data; return source && source->context.data;
@ -79,6 +82,7 @@ static const char *source_signals[] = {
"void show(ptr source)", "void show(ptr source)",
"void hide(ptr source)", "void hide(ptr source)",
"void volume(ptr source, in out float volume)", "void volume(ptr source, in out float volume)",
"void volumelevel(ptr source, in out float volume)",
NULL NULL
}; };
@ -568,8 +572,9 @@ static void source_output_audio_line(obs_source_t source,
in.timestamp += source->timing_adjust + source->sync_offset; in.timestamp += source->timing_adjust + source->sync_offset;
in.volume = source->user_volume * source->present_volume * in.volume = source->user_volume * source->present_volume *
obs->audio.user_volume * obs->audio.present_volume; obs->audio.user_volume * obs->audio.present_volume;
audio_line_output(source->audio_line, &in); int vol = audio_line_output(source->audio_line, &in);
obs_source_updatevolumelevel(source, vol);
} }
enum convert_type { enum convert_type {
@ -1597,6 +1602,22 @@ 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, static void set_tree_preset_vol(obs_source_t parent, obs_source_t child,
void *param) void *param)
{ {

View file

@ -554,6 +554,9 @@ EXPORT proc_handler_t obs_source_prochandler(obs_source_t source);
/** Sets the user volume for a source that has audio output */ /** Sets the user volume for a source that has audio output */
EXPORT void obs_source_setvolume(obs_source_t source, float volume); EXPORT void obs_source_setvolume(obs_source_t source, float volume);
/** Updates live volume for a source */
EXPORT void obs_source_updatevolumelevel(obs_source_t source, int volume);
/** Sets the presentation volume for a source */ /** Sets the presentation volume for a source */
EXPORT void obs_source_set_present_volume(obs_source_t source, float volume); EXPORT void obs_source_set_present_volume(obs_source_t source, float volume);

View file

@ -17,6 +17,19 @@ void VolControl::OBSVolumeChanged(void *data, calldata_t calldata)
Q_ARG(int, vol)); Q_ARG(int, vol));
} }
// [Danni] This may be a bit too resource intensive for such a simple
// application.
void VolControl::OBSVolumeLevel(void *data, calldata_t calldata)
{
VolControl *volControl = static_cast<VolControl*>(data);
int v = calldata_int(calldata, "volumelevel");
QMetaObject::invokeMethod(volControl, "VolumeLevel",
Q_ARG(int, v));
}
void VolControl::VolumeChanged(int vol) void VolControl::VolumeChanged(int vol)
{ {
signalChanged = false; signalChanged = false;
@ -24,6 +37,11 @@ void VolControl::VolumeChanged(int vol)
signalChanged = true; signalChanged = true;
} }
void VolControl::VolumeLevel(int vol)
{
volMeter->setValue(vol); /* linear */
}
void VolControl::SliderChanged(int vol) void VolControl::SliderChanged(int vol)
{ {
if (signalChanged) { if (signalChanged) {
@ -48,6 +66,7 @@ VolControl::VolControl(OBSSource source_)
nameLabel = new QLabel(); nameLabel = new QLabel();
volLabel = new QLabel(); volLabel = new QLabel();
volMeter = new QProgressBar();
slider = new QSlider(Qt::Horizontal); slider = new QSlider(Qt::Horizontal);
QFont font = nameLabel->font(); QFont font = nameLabel->font();
@ -60,8 +79,17 @@ VolControl::VolControl(OBSSource source_)
slider->setMinimum(0); slider->setMinimum(0);
slider->setMaximum(100); slider->setMaximum(100);
slider->setValue(vol); slider->setValue(vol);
//slider->setMaximumHeight(16); slider->setMaximumHeight(10);
volMeter->setMaximumHeight(1);
volMeter->setMinimum(0);
volMeter->setMaximum(10000);
volMeter->setTextVisible(false);
// [Danni] Temporary color.
QString testColor = "QProgressBar {border: 0px} QProgressBar::chunk {width: 1px; background-color: #AA0000;}";
volMeter->setStyleSheet(testColor);
textLayout->setContentsMargins(0, 0, 0, 0); textLayout->setContentsMargins(0, 0, 0, 0);
textLayout->addWidget(nameLabel); textLayout->addWidget(nameLabel);
textLayout->addWidget(volLabel); textLayout->addWidget(volLabel);
@ -71,6 +99,7 @@ VolControl::VolControl(OBSSource source_)
mainLayout->setContentsMargins(4, 4, 4, 4); mainLayout->setContentsMargins(4, 4, 4, 4);
mainLayout->setSpacing(2); mainLayout->setSpacing(2);
mainLayout->addItem(textLayout); mainLayout->addItem(textLayout);
mainLayout->addWidget(volMeter);
mainLayout->addWidget(slider); mainLayout->addWidget(slider);
setLayout(mainLayout); setLayout(mainLayout);
@ -78,6 +107,9 @@ VolControl::VolControl(OBSSource source_)
signal_handler_connect(obs_source_signalhandler(source), signal_handler_connect(obs_source_signalhandler(source),
"volume", OBSVolumeChanged, this); "volume", OBSVolumeChanged, this);
signal_handler_connect(obs_source_signalhandler(source),
"volumelevel", OBSVolumeLevel, this);
QWidget::connect(slider, SIGNAL(valueChanged(int)), QWidget::connect(slider, SIGNAL(valueChanged(int)),
this, SLOT(SliderChanged(int))); this, SLOT(SliderChanged(int)));
} }
@ -86,4 +118,7 @@ VolControl::~VolControl()
{ {
signal_handler_disconnect(obs_source_signalhandler(source), signal_handler_disconnect(obs_source_signalhandler(source),
"volume", OBSVolumeChanged, this); "volume", OBSVolumeChanged, this);
signal_handler_disconnect(obs_source_signalhandler(source),
"volumelevel", OBSVolumeLevel, this);
} }

View file

@ -2,6 +2,7 @@
#include <obs.hpp> #include <obs.hpp>
#include <QWidget> #include <QWidget>
#include <QProgressBar>
/* TODO: Make a real volume control that isn't terrible */ /* TODO: Make a real volume control that isn't terrible */
@ -13,15 +14,18 @@ class VolControl : public QWidget {
private: private:
OBSSource source; OBSSource source;
QLabel *nameLabel; QLabel *nameLabel;
QLabel *volLabel; QLabel *volLabel;
QSlider *slider; QProgressBar *volMeter;
bool signalChanged; QSlider *slider;
bool signalChanged;
static void OBSVolumeChanged(void *param, calldata_t calldata); static void OBSVolumeChanged(void *param, calldata_t calldata);
static void OBSVolumeLevel(void *data, calldata_t calldata);
private slots: private slots:
void VolumeChanged(int vol); void VolumeChanged(int vol);
void VolumeLevel(int vol);
void SliderChanged(int vol); void SliderChanged(int vol);
public: public: