Added simple volume meter for reference of input levels.
This commit is contained in:
parent
1e525c4713
commit
bc542a3e75
@ -713,8 +713,9 @@ uint32_t audio_output_samplerate(audio_t audio)
|
||||
|
||||
/* 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;
|
||||
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 output = val * vol / 127;
|
||||
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;
|
||||
int64_t vol = (int64_t)(volume * 32767.0f);
|
||||
|
||||
for (size_t i = 0; i < total_num; i++) {
|
||||
int64_t output = (int64_t)vals[i] * vol / 32767;
|
||||
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)
|
||||
@ -756,19 +762,23 @@ static inline void conv_float_to_24bit(float fval, uint8_t *vals)
|
||||
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;
|
||||
|
||||
for (size_t i = 0; i < total_num; i++) {
|
||||
float val = conv_24bit_to_float(vals) * volume;
|
||||
conv_float_to_24bit(CLAMP(val, -1.0f, 1.0f), vals);
|
||||
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;
|
||||
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 output = val * dvol;
|
||||
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;
|
||||
|
||||
for (size_t i = 0; i < total_num; i++)
|
||||
for (size_t i = 0; i < total_num; i++) {
|
||||
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)
|
||||
{
|
||||
int maxVol = 0;
|
||||
|
||||
bool planar = line->audio->planes > 1;
|
||||
size_t total_num = data->frames * (planar ? 1 : line->audio->channels);
|
||||
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) {
|
||||
case AUDIO_FORMAT_U8BIT:
|
||||
case AUDIO_FORMAT_U8BIT_PLANAR:
|
||||
mul_vol_u8bit(array, data->volume, total_num);
|
||||
maxVol = mul_vol_u8bit(array, data->volume, total_num);
|
||||
break;
|
||||
case AUDIO_FORMAT_16BIT:
|
||||
case AUDIO_FORMAT_16BIT_PLANAR:
|
||||
mul_vol_16bit(array, data->volume, total_num);
|
||||
maxVol = mul_vol_16bit(array, data->volume, total_num);
|
||||
break;
|
||||
case AUDIO_FORMAT_32BIT:
|
||||
case AUDIO_FORMAT_32BIT_PLANAR:
|
||||
mul_vol_32bit(array, data->volume, total_num);
|
||||
maxVol = mul_vol_32bit(array, data->volume, total_num);
|
||||
break;
|
||||
case AUDIO_FORMAT_FLOAT:
|
||||
case AUDIO_FORMAT_FLOAT_PLANAR:
|
||||
mul_vol_float(array, data->volume, total_num);
|
||||
maxVol = mul_vol_float(array, data->volume, total_num);
|
||||
break;
|
||||
case AUDIO_FORMAT_UNKNOWN:
|
||||
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,
|
||||
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)
|
||||
{
|
||||
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);
|
||||
#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
|
||||
* audio timing */
|
||||
|
||||
if (!line || !data) return;
|
||||
if (!line || !data) return 0;
|
||||
|
||||
int maxVol = 0;
|
||||
pthread_mutex_lock(&line->mutex);
|
||||
|
||||
if (!line->buffers[0].size) {
|
||||
line->base_timestamp = data->timestamp -
|
||||
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) {
|
||||
audio_line_place_data(line, data);
|
||||
maxVol = audio_line_place_data(line, data);
|
||||
|
||||
} else {
|
||||
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);
|
||||
return maxVol;
|
||||
}
|
||||
|
@ -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 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
|
||||
|
@ -19,6 +19,7 @@
|
||||
|
||||
#include "media-io/format-conversion.h"
|
||||
#include "media-io/video-frame.h"
|
||||
#include "media-io/audio-io.h"
|
||||
#include "util/threading.h"
|
||||
#include "util/platform.h"
|
||||
#include "callback/calldata.h"
|
||||
@ -28,6 +29,8 @@
|
||||
#include "obs.h"
|
||||
#include "obs-internal.h"
|
||||
|
||||
|
||||
|
||||
static inline bool source_valid(struct obs_source *source)
|
||||
{
|
||||
return source && source->context.data;
|
||||
@ -79,6 +82,7 @@ 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 float volume)",
|
||||
NULL
|
||||
};
|
||||
|
||||
@ -568,8 +572,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;
|
||||
|
||||
audio_line_output(source->audio_line, &in);
|
||||
|
||||
int vol = audio_line_output(source->audio_line, &in);
|
||||
obs_source_updatevolumelevel(source, vol);
|
||||
}
|
||||
|
||||
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,
|
||||
void *param)
|
||||
{
|
||||
|
@ -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 */
|
||||
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 */
|
||||
EXPORT void obs_source_set_present_volume(obs_source_t source, float volume);
|
||||
|
||||
|
@ -17,6 +17,19 @@ void VolControl::OBSVolumeChanged(void *data, calldata_t calldata)
|
||||
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)
|
||||
{
|
||||
signalChanged = false;
|
||||
@ -24,6 +37,11 @@ void VolControl::VolumeChanged(int vol)
|
||||
signalChanged = true;
|
||||
}
|
||||
|
||||
void VolControl::VolumeLevel(int vol)
|
||||
{
|
||||
volMeter->setValue(vol); /* linear */
|
||||
}
|
||||
|
||||
void VolControl::SliderChanged(int vol)
|
||||
{
|
||||
if (signalChanged) {
|
||||
@ -48,6 +66,7 @@ VolControl::VolControl(OBSSource source_)
|
||||
|
||||
nameLabel = new QLabel();
|
||||
volLabel = new QLabel();
|
||||
volMeter = new QProgressBar();
|
||||
slider = new QSlider(Qt::Horizontal);
|
||||
|
||||
QFont font = nameLabel->font();
|
||||
@ -60,8 +79,17 @@ VolControl::VolControl(OBSSource source_)
|
||||
slider->setMinimum(0);
|
||||
slider->setMaximum(100);
|
||||
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->addWidget(nameLabel);
|
||||
textLayout->addWidget(volLabel);
|
||||
@ -71,6 +99,7 @@ VolControl::VolControl(OBSSource source_)
|
||||
mainLayout->setContentsMargins(4, 4, 4, 4);
|
||||
mainLayout->setSpacing(2);
|
||||
mainLayout->addItem(textLayout);
|
||||
mainLayout->addWidget(volMeter);
|
||||
mainLayout->addWidget(slider);
|
||||
|
||||
setLayout(mainLayout);
|
||||
@ -78,6 +107,9 @@ VolControl::VolControl(OBSSource source_)
|
||||
signal_handler_connect(obs_source_signalhandler(source),
|
||||
"volume", OBSVolumeChanged, this);
|
||||
|
||||
signal_handler_connect(obs_source_signalhandler(source),
|
||||
"volumelevel", OBSVolumeLevel, this);
|
||||
|
||||
QWidget::connect(slider, SIGNAL(valueChanged(int)),
|
||||
this, SLOT(SliderChanged(int)));
|
||||
}
|
||||
@ -86,4 +118,7 @@ VolControl::~VolControl()
|
||||
{
|
||||
signal_handler_disconnect(obs_source_signalhandler(source),
|
||||
"volume", OBSVolumeChanged, this);
|
||||
|
||||
signal_handler_disconnect(obs_source_signalhandler(source),
|
||||
"volumelevel", OBSVolumeLevel, this);
|
||||
}
|
||||
|
@ -2,6 +2,7 @@
|
||||
|
||||
#include <obs.hpp>
|
||||
#include <QWidget>
|
||||
#include <QProgressBar>
|
||||
|
||||
/* TODO: Make a real volume control that isn't terrible */
|
||||
|
||||
@ -13,15 +14,18 @@ class VolControl : public QWidget {
|
||||
|
||||
private:
|
||||
OBSSource source;
|
||||
QLabel *nameLabel;
|
||||
QLabel *volLabel;
|
||||
QSlider *slider;
|
||||
bool signalChanged;
|
||||
QLabel *nameLabel;
|
||||
QLabel *volLabel;
|
||||
QProgressBar *volMeter;
|
||||
QSlider *slider;
|
||||
bool signalChanged;
|
||||
|
||||
static void OBSVolumeChanged(void *param, calldata_t calldata);
|
||||
static void OBSVolumeLevel(void *data, calldata_t calldata);
|
||||
|
||||
private slots:
|
||||
void VolumeChanged(int vol);
|
||||
void VolumeLevel(int vol);
|
||||
void SliderChanged(int vol);
|
||||
|
||||
public:
|
||||
|
Loading…
x
Reference in New Issue
Block a user