Merge pull request #90 from antihax/master
Added simple volume meter for reference of input levels.
This commit is contained in:
commit
4eb6267372
@ -173,11 +173,14 @@ void signal_handler_connect(signal_handler_t handler, const char *signal,
|
||||
|
||||
pthread_mutex_lock(&handler->mutex);
|
||||
sig = getsignal(handler, signal, &last);
|
||||
if (!sig)
|
||||
return;
|
||||
|
||||
pthread_mutex_unlock(&handler->mutex);
|
||||
|
||||
if (!sig) {
|
||||
blog(LOG_WARNING, "signal_handler_connect: "
|
||||
"signal '%s' not found", signal);
|
||||
return;
|
||||
}
|
||||
|
||||
/* -------------- */
|
||||
|
||||
pthread_mutex_lock(&sig->mutex);
|
||||
|
@ -181,78 +181,9 @@ static inline size_t min_size(size_t a, size_t b)
|
||||
((val > maxval) ? maxval : ((val < minval) ? minval : val))
|
||||
#endif
|
||||
|
||||
#define MIN_S8 -128
|
||||
#define MAX_S8 127
|
||||
#define MIN_S16 -32767
|
||||
#define MAX_S16 32767
|
||||
#define MIN_S32 -2147483647
|
||||
#define MAX_S32 2147483647
|
||||
|
||||
#define MIX_BUFFER_SIZE 256
|
||||
|
||||
/* TODO: optimize mixing */
|
||||
static void mix_u8(uint8_t *mix, struct circlebuf *buf, size_t size)
|
||||
{
|
||||
uint8_t vals[MIX_BUFFER_SIZE];
|
||||
register int16_t mix_val;
|
||||
|
||||
while (size) {
|
||||
size_t pop_count = min_size(size, sizeof(vals));
|
||||
size -= pop_count;
|
||||
|
||||
circlebuf_pop_front(buf, vals, pop_count);
|
||||
|
||||
for (size_t i = 0; i < pop_count; i++) {
|
||||
mix_val = (int16_t)*mix - 128;
|
||||
mix_val += (int16_t)vals[i] - 128;
|
||||
mix_val = CLAMP(mix_val, MIN_S8, MAX_S8) + 128;
|
||||
*(mix++) = (uint8_t)mix_val;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void mix_s16(uint8_t *mix_in, struct circlebuf *buf, size_t size)
|
||||
{
|
||||
int16_t *mix = (int16_t*)mix_in;
|
||||
int16_t vals[MIX_BUFFER_SIZE];
|
||||
register int32_t mix_val;
|
||||
|
||||
while (size) {
|
||||
size_t pop_count = min_size(size, sizeof(vals));
|
||||
size -= pop_count;
|
||||
|
||||
circlebuf_pop_front(buf, vals, pop_count);
|
||||
pop_count /= sizeof(int16_t);
|
||||
|
||||
for (size_t i = 0; i < pop_count; i++) {
|
||||
mix_val = (int32_t)*mix;
|
||||
mix_val += (int32_t)vals[i];
|
||||
*(mix++) = (int16_t)CLAMP(mix_val, MIN_S16, MAX_S16);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void mix_s32(uint8_t *mix_in, struct circlebuf *buf, size_t size)
|
||||
{
|
||||
int32_t *mix = (int32_t*)mix_in;
|
||||
int32_t vals[MIX_BUFFER_SIZE];
|
||||
register int64_t mix_val;
|
||||
|
||||
while (size) {
|
||||
size_t pop_count = min_size(size, sizeof(vals));
|
||||
size -= pop_count;
|
||||
|
||||
circlebuf_pop_front(buf, vals, pop_count);
|
||||
pop_count /= sizeof(int32_t);
|
||||
|
||||
for (size_t i = 0; i < pop_count; i++) {
|
||||
mix_val = (int64_t)*mix;
|
||||
mix_val += (int64_t)vals[i];
|
||||
*(mix++) = (int32_t)CLAMP(mix_val, MIN_S32, MAX_S32);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void mix_float(uint8_t *mix_in, struct circlebuf *buf, size_t size)
|
||||
{
|
||||
float *mix = (float*)mix_in;
|
||||
@ -273,31 +204,6 @@ static void mix_float(uint8_t *mix_in, struct circlebuf *buf, size_t size)
|
||||
}
|
||||
}
|
||||
|
||||
static inline void mix_audio(enum audio_format format,
|
||||
uint8_t *mix, struct circlebuf *buf, size_t size)
|
||||
{
|
||||
switch (format) {
|
||||
case AUDIO_FORMAT_UNKNOWN:
|
||||
break;
|
||||
|
||||
case AUDIO_FORMAT_U8BIT:
|
||||
case AUDIO_FORMAT_U8BIT_PLANAR:
|
||||
mix_u8(mix, buf, size); break;
|
||||
|
||||
case AUDIO_FORMAT_16BIT:
|
||||
case AUDIO_FORMAT_16BIT_PLANAR:
|
||||
mix_s16(mix, buf, size); break;
|
||||
|
||||
case AUDIO_FORMAT_32BIT:
|
||||
case AUDIO_FORMAT_32BIT_PLANAR:
|
||||
mix_s32(mix, buf, size); break;
|
||||
|
||||
case AUDIO_FORMAT_FLOAT:
|
||||
case AUDIO_FORMAT_FLOAT_PLANAR:
|
||||
mix_float(mix, buf, size); break;
|
||||
}
|
||||
}
|
||||
|
||||
static inline bool mix_audio_line(struct audio_output *audio,
|
||||
struct audio_line *line, size_t size, uint64_t timestamp)
|
||||
{
|
||||
@ -315,8 +221,7 @@ static inline bool mix_audio_line(struct audio_output *audio,
|
||||
for (size_t i = 0; i < audio->planes; i++) {
|
||||
size_t pop_size = min_size(size, line->buffers[i].size);
|
||||
|
||||
mix_audio(audio->info.format,
|
||||
audio->mix_buffers[i].array + time_offset,
|
||||
mix_float(audio->mix_buffers[i].array + time_offset,
|
||||
&line->buffers[i], pop_size);
|
||||
}
|
||||
|
||||
@ -711,80 +616,11 @@ uint32_t audio_output_samplerate(audio_t audio)
|
||||
return audio ? audio->info.samples_per_sec : 0;
|
||||
}
|
||||
|
||||
/* TODO: Optimization of volume multiplication functions */
|
||||
|
||||
static inline void mul_vol_u8bit(void *array, float volume, size_t total_num)
|
||||
/* TODO: optimize these two functions */
|
||||
static inline void mul_vol_float(float *array, float volume, size_t count)
|
||||
{
|
||||
uint8_t *vals = array;
|
||||
int32_t vol = (int32_t)(volume * 127.0f);
|
||||
|
||||
for (size_t i = 0; i < total_num; i++) {
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
static inline void mul_vol_16bit(void *array, float volume, size_t total_num)
|
||||
{
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
static inline float conv_24bit_to_float(uint8_t *vals)
|
||||
{
|
||||
int32_t val = ((int32_t)vals[0]) |
|
||||
((int32_t)vals[1] << 8) |
|
||||
((int32_t)vals[2] << 16);
|
||||
|
||||
if ((val & 0x800000) != 0)
|
||||
val |= 0xFF000000;
|
||||
|
||||
return (float)val / 8388607.0f;
|
||||
}
|
||||
|
||||
static inline void conv_float_to_24bit(float fval, uint8_t *vals)
|
||||
{
|
||||
int32_t val = (int32_t)(fval * 8388607.0f);
|
||||
vals[0] = (val) & 0xFF;
|
||||
vals[1] = (val >> 8) & 0xFF;
|
||||
vals[2] = (val >> 16) & 0xFF;
|
||||
}
|
||||
|
||||
static inline void mul_vol_24bit(void *array, float volume, size_t total_num)
|
||||
{
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
static inline void mul_vol_32bit(void *array, float volume, size_t total_num)
|
||||
{
|
||||
int32_t *vals = array;
|
||||
double dvol = (double)volume;
|
||||
|
||||
for (size_t i = 0; i < total_num; i++) {
|
||||
double val = (double)vals[i] / 2147483647.0;
|
||||
double output = val * dvol;
|
||||
vals[i] = (int32_t)(CLAMP(output, -1.0, 1.0) * 2147483647.0);
|
||||
}
|
||||
}
|
||||
|
||||
static inline void mul_vol_float(void *array, float volume, size_t total_num)
|
||||
{
|
||||
float *vals = array;
|
||||
|
||||
for (size_t i = 0; i < total_num; i++)
|
||||
vals[i] *= volume;
|
||||
for (size_t i = 0; i < count; i++)
|
||||
array[i] *= volume;
|
||||
}
|
||||
|
||||
static void audio_line_place_data_pos(struct audio_line *line,
|
||||
@ -801,25 +637,13 @@ static void audio_line_place_data_pos(struct audio_line *line,
|
||||
uint8_t *array = line->volume_buffers[i].array;
|
||||
|
||||
switch (line->audio->info.format) {
|
||||
case AUDIO_FORMAT_U8BIT:
|
||||
case AUDIO_FORMAT_U8BIT_PLANAR:
|
||||
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);
|
||||
break;
|
||||
case AUDIO_FORMAT_32BIT:
|
||||
case AUDIO_FORMAT_32BIT_PLANAR:
|
||||
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);
|
||||
mul_vol_float((float*)array, data->volume, total_num);
|
||||
break;
|
||||
case AUDIO_FORMAT_UNKNOWN:
|
||||
blog(LOG_ERROR, "audio_line_place_data_pos: "
|
||||
"Unknown format");
|
||||
"Unsupported or unknown format");
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -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,
|
||||
|
@ -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"
|
||||
@ -79,6 +80,8 @@ static const char *source_signals[] = {
|
||||
"void show(ptr source)",
|
||||
"void hide(ptr source)",
|
||||
"void volume(ptr source, in out float volume)",
|
||||
"void volume_level(ptr source, float level, float magnitude, "
|
||||
"float peak)",
|
||||
NULL
|
||||
};
|
||||
|
||||
@ -525,10 +528,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)
|
||||
{
|
||||
@ -569,6 +645,7 @@ static void source_output_audio_line(obs_source_t source,
|
||||
obs->audio.user_volume * obs->audio.present_volume;
|
||||
|
||||
audio_line_output(source->audio_line, &in);
|
||||
obs_source_update_volume_level(source, &in);
|
||||
}
|
||||
|
||||
enum convert_type {
|
||||
@ -894,13 +971,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);
|
||||
|
@ -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)",
|
||||
|
@ -525,6 +525,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);
|
||||
|
||||
|
@ -1,20 +1,57 @@
|
||||
#include "volume-control.hpp"
|
||||
#include "qt-wrappers.hpp"
|
||||
#include <util/platform.h>
|
||||
#include <QHBoxLayout>
|
||||
#include <QVBoxLayout>
|
||||
#include <QSlider>
|
||||
#include <QLabel>
|
||||
#include <string>
|
||||
#include <math.h>
|
||||
|
||||
using namespace std;
|
||||
|
||||
#define VOL_MIN -96.0f
|
||||
#define VOL_MAX 0.0f
|
||||
|
||||
#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)
|
||||
{
|
||||
VolControl *volControl = static_cast<VolControl*>(data);
|
||||
int vol = (int)(calldata_float(calldata, "volume") * 100.0f + 0.5f);
|
||||
|
||||
QMetaObject::invokeMethod(volControl, "VolumeChanged",
|
||||
Q_ARG(int, vol));
|
||||
QMetaObject::invokeMethod(volControl, "VolumeChanged", Q_ARG(int, vol));
|
||||
}
|
||||
|
||||
void VolControl::OBSVolumeLevel(void *data, calldata_t calldata)
|
||||
{
|
||||
VolControl *volControl = static_cast<VolControl*>(data);
|
||||
float level = calldata_float(calldata, "level");
|
||||
float mag = calldata_float(calldata, "magnitude");
|
||||
|
||||
/*
|
||||
* TODO: an actual volume control that can process level, mag, peak.
|
||||
*
|
||||
* for the time being, just average level and magnitude.
|
||||
*/
|
||||
float result = (level + mag) * 0.5f;
|
||||
|
||||
QMetaObject::invokeMethod(volControl, "VolumeLevel",
|
||||
Q_ARG(float, result));
|
||||
}
|
||||
|
||||
void VolControl::VolumeChanged(int vol)
|
||||
@ -24,6 +61,25 @@ void VolControl::VolumeChanged(int vol)
|
||||
signalChanged = true;
|
||||
}
|
||||
|
||||
void VolControl::VolumeLevel(float level)
|
||||
{
|
||||
uint64_t curMeterTime = os_gettime_ns() / 1000000;
|
||||
|
||||
levelTotal += level;
|
||||
levelCount += 1.0f;
|
||||
|
||||
/* only update after a certain amount of time */
|
||||
if ((curMeterTime - lastMeterTime) > UPDATE_INTERVAL_MS) {
|
||||
lastMeterTime = curMeterTime;
|
||||
|
||||
float finalLevel = levelTotal / levelCount;
|
||||
volMeter->setValue(int(DBToLinear(finalLevel) * 10000.0f));
|
||||
|
||||
levelTotal = 0.0f;
|
||||
levelCount = 0.0f;
|
||||
}
|
||||
}
|
||||
|
||||
void VolControl::SliderChanged(int vol)
|
||||
{
|
||||
if (signalChanged) {
|
||||
@ -35,12 +91,16 @@ void VolControl::SliderChanged(int vol)
|
||||
signal_handler_connect(obs_source_signalhandler(source),
|
||||
"volume", OBSVolumeChanged, this);
|
||||
}
|
||||
|
||||
volLabel->setText(QString::number(vol));
|
||||
}
|
||||
|
||||
VolControl::VolControl(OBSSource source_)
|
||||
: source (source_),
|
||||
signalChanged (true)
|
||||
signalChanged (true),
|
||||
lastMeterTime (0),
|
||||
levelTotal (0.0f),
|
||||
levelCount (0.0f)
|
||||
{
|
||||
QVBoxLayout *mainLayout = new QVBoxLayout();
|
||||
QHBoxLayout *textLayout = new QHBoxLayout();
|
||||
@ -48,6 +108,7 @@ VolControl::VolControl(OBSSource source_)
|
||||
|
||||
nameLabel = new QLabel();
|
||||
volLabel = new QLabel();
|
||||
volMeter = new QProgressBar();
|
||||
slider = new QSlider(Qt::Horizontal);
|
||||
|
||||
QFont font = nameLabel->font();
|
||||
@ -60,7 +121,19 @@ VolControl::VolControl(OBSSource source_)
|
||||
slider->setMinimum(0);
|
||||
slider->setMaximum(100);
|
||||
slider->setValue(vol);
|
||||
//slider->setMaximumHeight(16);
|
||||
// slider->setMaximumHeight(13);
|
||||
|
||||
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);
|
||||
@ -71,6 +144,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 +152,9 @@ VolControl::VolControl(OBSSource source_)
|
||||
signal_handler_connect(obs_source_signalhandler(source),
|
||||
"volume", OBSVolumeChanged, this);
|
||||
|
||||
signal_handler_connect(obs_source_signalhandler(source),
|
||||
"volume_level", OBSVolumeLevel, this);
|
||||
|
||||
QWidget::connect(slider, SIGNAL(valueChanged(int)),
|
||||
this, SLOT(SliderChanged(int)));
|
||||
}
|
||||
@ -86,4 +163,7 @@ VolControl::~VolControl()
|
||||
{
|
||||
signal_handler_disconnect(obs_source_signalhandler(source),
|
||||
"volume", OBSVolumeChanged, this);
|
||||
|
||||
signal_handler_disconnect(obs_source_signalhandler(source),
|
||||
"volume_level", 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,21 @@ 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;
|
||||
uint64_t lastMeterTime;
|
||||
float levelTotal;
|
||||
float levelCount;
|
||||
|
||||
static void OBSVolumeChanged(void *param, calldata_t calldata);
|
||||
static void OBSVolumeLevel(void *data, calldata_t calldata);
|
||||
|
||||
private slots:
|
||||
void VolumeChanged(int vol);
|
||||
void VolumeLevel(float level);
|
||||
void SliderChanged(int vol);
|
||||
|
||||
public:
|
||||
|
Loading…
x
Reference in New Issue
Block a user