diff --git a/libobs/obs-properties.h b/libobs/obs-properties.h index 2cc1f12f0..c8057b2ab 100644 --- a/libobs/obs-properties.h +++ b/libobs/obs-properties.h @@ -78,7 +78,7 @@ enum obs_editable_list_type { OBS_EDITABLE_LIST_TYPE_STRINGS, OBS_EDITABLE_LIST_TYPE_FILES, OBS_EDITABLE_LIST_TYPE_FILES_AND_URLS, - OBS_EDITABLE_LIST_TYPE_SOURCES, + OBS_EDITABLE_LIST_TYPE_SOURCES, }; enum obs_path_type { diff --git a/plugins/obs-filters/compressor-filter.c b/plugins/obs-filters/compressor-filter.c index 6e03f9bf2..37b2cceb4 100644 --- a/plugins/obs-filters/compressor-filter.c +++ b/plugins/obs-filters/compressor-filter.c @@ -61,14 +61,14 @@ /* -------------------------------------------------------- */ struct sidechain { - pthread_mutex_t mutex; - struct deque data[MAX_AUDIO_CHANNELS]; - float *buf[MAX_AUDIO_CHANNELS]; - char *name; - obs_weak_source_t *weak_ref; - uint64_t check_time; - size_t max_frames; - size_t num_channels; + pthread_mutex_t mutex; + struct deque data[MAX_AUDIO_CHANNELS]; + float *buf[MAX_AUDIO_CHANNELS]; + char *name; + obs_weak_source_t *weak_ref; + uint64_t check_time; + size_t max_frames; + size_t num_channels; }; struct compressor_data { @@ -88,43 +88,42 @@ struct compressor_data { float slope; pthread_mutex_t sidechain_update_mutex; - + size_t max_sidechain_frames; - - DARRAY(struct sidechain) sidechains; + + DARRAY(struct sidechain) sidechains; }; /* -------------------------------------------------------- */ - static inline void get_sidechain_data(struct compressor_data *cd, const uint32_t num_samples) { size_t data_size = cd->envelope_buf_len * sizeof(float); if (!data_size) return; - - for (size_t ix = 0; ix < cd->sidechains.num; ix += 1) { - struct sidechain *sidechain = &cd->sidechains.array[ix]; - pthread_mutex_lock(&sidechain->mutex); - if (sidechain->max_frames < num_samples) - sidechain->max_frames = num_samples; - - if (sidechain->data[0].size < data_size) { - for (size_t i = 0; i < cd->num_channels; i++) { - memset(sidechain->buf[i], 0, data_size); - } - pthread_mutex_unlock(&sidechain->mutex); - continue; - } - - for (size_t i = 0; i < cd->num_channels; i++) - deque_pop_front(&sidechain->data[i], sidechain->buf[i], - data_size); - - pthread_mutex_unlock(&sidechain->mutex); - } + for (size_t ix = 0; ix < cd->sidechains.num; ix += 1) { + struct sidechain *sidechain = &cd->sidechains.array[ix]; + + pthread_mutex_lock(&sidechain->mutex); + if (sidechain->max_frames < num_samples) + sidechain->max_frames = num_samples; + + if (sidechain->data[0].size < data_size) { + for (size_t i = 0; i < cd->num_channels; i++) { + memset(sidechain->buf[i], 0, data_size); + } + pthread_mutex_unlock(&sidechain->mutex); + continue; + } + + for (size_t i = 0; i < cd->num_channels; i++) + deque_pop_front(&sidechain->data[i], sidechain->buf[i], + data_size); + + pthread_mutex_unlock(&sidechain->mutex); + } } static void resize_env_buffer(struct compressor_data *cd, size_t len) @@ -132,16 +131,16 @@ static void resize_env_buffer(struct compressor_data *cd, size_t len) cd->envelope_buf_len = len; cd->envelope_buf = brealloc(cd->envelope_buf, len * sizeof(float)); - for (size_t ix = 0; ix < cd->sidechains.num; ix += 1) { - struct sidechain *sidechain = &cd->sidechains.array[ix]; - pthread_mutex_lock(&sidechain->mutex); - for (size_t i = 0; i < cd->num_channels; i++) { - - sidechain->buf[i] = - brealloc(sidechain->buf[i], len * sizeof(float)); - } - pthread_mutex_unlock(&sidechain->mutex); - } + for (size_t ix = 0; ix < cd->sidechains.num; ix += 1) { + struct sidechain *sidechain = &cd->sidechains.array[ix]; + pthread_mutex_lock(&sidechain->mutex); + for (size_t i = 0; i < cd->num_channels; i++) { + + sidechain->buf[i] = brealloc(sidechain->buf[i], + len * sizeof(float)); + } + pthread_mutex_unlock(&sidechain->mutex); + } } static inline float gain_coefficient(uint32_t sample_rate, float time) @@ -158,7 +157,7 @@ static const char *compressor_name(void *unused) static void sidechain_capture(void *param, obs_source_t *source, const struct audio_data *audio_data, bool muted) { - struct sidechain *sidechain = param; + struct sidechain *sidechain = param; UNUSED_PARAMETER(source); @@ -197,37 +196,40 @@ unlock: pthread_mutex_unlock(&sidechain->mutex); } -static void clear_sidechains(struct compressor_data *cd) { - size_t sidechain_count = cd->sidechains.num; - for (size_t ix = 0; ix < sidechain_count; ix += 1) { - struct sidechain *sidechain = &cd->sidechains.array[ix]; - - obs_source_t *old_source = obs_weak_source_get_source(sidechain->weak_ref); - - if (old_source) { - obs_source_remove_audio_capture_callback(old_source, sidechain_capture, sidechain); - obs_source_release(old_source); - } - - if (sidechain->weak_ref) { - obs_weak_source_release(sidechain->weak_ref); - sidechain->weak_ref = NULL; - } - - if (sidechain->name) { - bfree(sidechain->name); - sidechain->name = NULL; - } - - for (size_t ix = 0; ix < MAX_AUDIO_CHANNELS; ix += 1) { - deque_free(&sidechain->data[ix]); - bfree(sidechain->buf[ix]); - } - - pthread_mutex_destroy(&sidechain->mutex); - } - - da_clear(cd->sidechains); +static void clear_sidechains(struct compressor_data *cd) +{ + size_t sidechain_count = cd->sidechains.num; + for (size_t ix = 0; ix < sidechain_count; ix += 1) { + struct sidechain *sidechain = &cd->sidechains.array[ix]; + + obs_source_t *old_source = + obs_weak_source_get_source(sidechain->weak_ref); + + if (old_source) { + obs_source_remove_audio_capture_callback( + old_source, sidechain_capture, sidechain); + obs_source_release(old_source); + } + + if (sidechain->weak_ref) { + obs_weak_source_release(sidechain->weak_ref); + sidechain->weak_ref = NULL; + } + + if (sidechain->name) { + bfree(sidechain->name); + sidechain->name = NULL; + } + + for (size_t ix = 0; ix < MAX_AUDIO_CHANNELS; ix += 1) { + deque_free(&sidechain->data[ix]); + bfree(sidechain->buf[ix]); + } + + pthread_mutex_destroy(&sidechain->mutex); + } + + da_clear(cd->sidechains); } static void compressor_update(void *data, obs_data_t *s) @@ -242,9 +244,9 @@ static void compressor_update(void *data, obs_data_t *s) (float)obs_data_get_int(s, S_RELEASE_TIME); const float output_gain_db = (float)obs_data_get_double(s, S_OUTPUT_GAIN); - obs_data_array_t *sidechain_names = - obs_data_get_array(s, S_SIDECHAIN_SOURCE); - size_t sidechain_count = obs_data_array_count(sidechain_names); + obs_data_array_t *sidechain_names = + obs_data_get_array(s, S_SIDECHAIN_SOURCE); + size_t sidechain_count = obs_data_array_count(sidechain_names); cd->ratio = (float)obs_data_get_double(s, S_RATIO); cd->threshold = (float)obs_data_get_double(s, S_THRESHOLD); @@ -256,44 +258,44 @@ static void compressor_update(void *data, obs_data_t *s) cd->num_channels = num_channels; cd->sample_rate = sample_rate; cd->slope = 1.0f - (1.0f / cd->ratio); - - // reset callbacks and cleanup after ourselves before updating list - pthread_mutex_lock(&cd->sidechain_update_mutex); - clear_sidechains(cd); - pthread_mutex_unlock(&cd->sidechain_update_mutex); - - size_t erased_count = 0; - for (size_t ix = 0; ix < sidechain_count; ix += 1) { - obs_data_t *source = obs_data_array_item(sidechain_names, ix); - const char *sidechain_name = obs_data_get_string(source, "value"); - - bool valid_sidechain = *sidechain_name && - strcmp(sidechain_name, "none") != 0; - if (!valid_sidechain) { - continue; - } - - pthread_mutex_lock(&cd->sidechain_update_mutex); - - struct sidechain *sidechain = da_push_back_new(cd->sidechains); - if (pthread_mutex_init(&sidechain->mutex, NULL) != 0) { - blog(LOG_ERROR, "Failed to create mutex for sidechain"); - da_erase(cd->sidechains, (ix - erased_count)); - erased_count += 1; - } - - sidechain->num_channels = num_channels; - sidechain->name = bstrdup(sidechain_name); - sidechain->weak_ref = NULL; - - pthread_mutex_unlock(&cd->sidechain_update_mutex); - - } + // reset callbacks and cleanup after ourselves before updating list + pthread_mutex_lock(&cd->sidechain_update_mutex); + clear_sidechains(cd); + pthread_mutex_unlock(&cd->sidechain_update_mutex); + + size_t erased_count = 0; + for (size_t ix = 0; ix < sidechain_count; ix += 1) { + obs_data_t *source = obs_data_array_item(sidechain_names, ix); + const char *sidechain_name = + obs_data_get_string(source, "value"); + + bool valid_sidechain = *sidechain_name && + strcmp(sidechain_name, "none") != 0; + if (!valid_sidechain) { + continue; + } + + pthread_mutex_lock(&cd->sidechain_update_mutex); + + struct sidechain *sidechain = da_push_back_new(cd->sidechains); + + if (pthread_mutex_init(&sidechain->mutex, NULL) != 0) { + blog(LOG_ERROR, "Failed to create mutex for sidechain"); + da_erase(cd->sidechains, (ix - erased_count)); + erased_count += 1; + } + + sidechain->num_channels = num_channels; + sidechain->name = bstrdup(sidechain_name); + sidechain->weak_ref = NULL; + + pthread_mutex_unlock(&cd->sidechain_update_mutex); + } size_t sample_len = sample_rate * DEFAULT_AUDIO_BUF_MS / MS_IN_S; - - resize_env_buffer(cd, sample_len); + + resize_env_buffer(cd, sample_len); } static void *compressor_create(obs_data_t *settings, obs_source_t *filter) @@ -307,8 +309,8 @@ static void *compressor_create(obs_data_t *settings, obs_source_t *filter) bfree(cd); return NULL; } - - da_init(cd->sidechains); + + da_init(cd->sidechains); compressor_update(cd, settings); return cd; @@ -316,16 +318,16 @@ static void *compressor_create(obs_data_t *settings, obs_source_t *filter) static void compressor_destroy(void *data) { - + struct compressor_data *cd = data; - pthread_mutex_destroy(&cd->sidechain_update_mutex); - - clear_sidechains(cd); - - da_free(cd->sidechains); - - bfree(cd->envelope_buf); + pthread_mutex_destroy(&cd->sidechain_update_mutex); + + clear_sidechains(cd); + + da_free(cd->sidechains); + + bfree(cd->envelope_buf); bfree(cd); } @@ -354,7 +356,6 @@ static void analyze_envelope(struct compressor_data *cd, float **samples, env = env_in + release_gain * (env - env_in); } envelope_buf[i] = fmaxf(envelope_buf[i], env); - } } cd->envelope = cd->envelope_buf[num_samples - 1]; @@ -371,35 +372,43 @@ static void analyze_sidechain(struct compressor_data *cd, const float attack_gain = cd->attack_gain; const float release_gain = cd->release_gain; - - memset(cd->envelope_buf, 0, num_samples * sizeof(cd->envelope_buf[0])); - for (size_t chan = 0; chan < cd->num_channels; ++chan) { - for (size_t sidechain_ix = 0; sidechain_ix < cd->sidechains.num; sidechain_ix += 1) { - float **sidechain_buf = cd->sidechains.array[sidechain_ix].buf; - if (!sidechain_buf[chan]) - continue; - - float *envelope_buf = cd->envelope_buf; - float env = cd->envelope; - for (uint32_t i = 0; i < num_samples; ++i) { - const float env_in = fabsf(sidechain_buf[chan][i]); - - if (env < env_in) { - env = env_in + attack_gain * (env - env_in); - } else { - env = env_in + release_gain * (env - env_in); - } - envelope_buf[i] = fmaxf(envelope_buf[i], env); - - } - // we don't need to check any other sidechains if we have successfully found one. - // TODO(Ben): the goal of this logic is to short-circuit the check, but this logic unfortunately - // is applied to a /muted/ source too... - //goto continue_outer; - } - //continue_outer:; - } + memset(cd->envelope_buf, 0, num_samples * sizeof(cd->envelope_buf[0])); + + for (size_t chan = 0; chan < cd->num_channels; ++chan) { + bool compression_applied = false; + for (size_t sidechain_ix = 0; sidechain_ix < cd->sidechains.num; + sidechain_ix += 1) { + float **sidechain_buf = + cd->sidechains.array[sidechain_ix].buf; + if (!sidechain_buf[chan]) + continue; + + float *envelope_buf = cd->envelope_buf; + float env = cd->envelope; + + for (uint32_t i = 0; i < num_samples; ++i) { + const float env_in = + fabsf(sidechain_buf[chan][i]); + + if (env < env_in) { + env = env_in + + attack_gain * (env - env_in); + compression_applied = true; + } else { + env = env_in + + release_gain * (env - env_in); + } + envelope_buf[i] = fmaxf(envelope_buf[i], env); + } + // if the threshold was reached on the channel already, there's no need to continue + // with other sidechains. + if (compression_applied) { + goto continue_outer; + } + } + continue_outer:; + } cd->envelope = cd->envelope_buf[num_samples - 1]; } @@ -421,28 +430,30 @@ static inline void process_compression(const struct compressor_data *cd, static void compressor_tick(void *data, float seconds) { - UNUSED_PARAMETER(seconds); + UNUSED_PARAMETER(seconds); - struct compressor_data *cd = data; - size_t sidechain_count = cd->sidechains.num; - for (size_t ix = 0; ix < sidechain_count; ix += 1) { - struct sidechain *sidechain = &cd->sidechains.array[ix]; - // is this a sidechain just created by update()? - if (sidechain->name && !sidechain->weak_ref) { - pthread_mutex_lock(&cd->sidechain_update_mutex); + struct compressor_data *cd = data; + size_t sidechain_count = cd->sidechains.num; + for (size_t ix = 0; ix < sidechain_count; ix += 1) { + struct sidechain *sidechain = &cd->sidechains.array[ix]; + // is this a sidechain just created by update()? + if (sidechain->name && !sidechain->weak_ref) { + pthread_mutex_lock(&cd->sidechain_update_mutex); - obs_source_t *source = obs_get_source_by_name(sidechain->name); - obs_weak_source_t *weak_sidechain = obs_source_get_weak_source(source); - - - sidechain->weak_ref = weak_sidechain; - - pthread_mutex_unlock(&cd->sidechain_update_mutex); - - obs_source_add_audio_capture_callback(source, sidechain_capture, sidechain); - obs_source_release(source); - } - } + obs_source_t *source = + obs_get_source_by_name(sidechain->name); + obs_weak_source_t *weak_sidechain = + obs_source_get_weak_source(source); + + sidechain->weak_ref = weak_sidechain; + + pthread_mutex_unlock(&cd->sidechain_update_mutex); + + obs_source_add_audio_capture_callback( + source, sidechain_capture, sidechain); + obs_source_release(source); + } + } } static struct obs_audio_data * @@ -457,15 +468,15 @@ compressor_filter_audio(void *data, struct obs_audio_data *audio) float **samples = (float **)audio->data; pthread_mutex_lock(&cd->sidechain_update_mutex); - bool has_sidechain = false; - const size_t sidechain_count = cd->sidechains.num; - for (size_t ix = 0; ix < sidechain_count; ix += 1) { - struct sidechain *sidechain = &cd->sidechains.array[ix]; - if (sidechain->weak_ref) { - has_sidechain = true; - break; - } - } + bool has_sidechain = false; + const size_t sidechain_count = cd->sidechains.num; + for (size_t ix = 0; ix < sidechain_count; ix += 1) { + struct sidechain *sidechain = &cd->sidechains.array[ix]; + if (sidechain->weak_ref) { + has_sidechain = true; + break; + } + } pthread_mutex_unlock(&cd->sidechain_update_mutex); if (has_sidechain) @@ -517,10 +528,10 @@ static obs_properties_t *compressor_properties(void *data) MAX_OUTPUT_GAIN_DB, 0.1); obs_property_float_set_suffix(p, " dB"); - obs_properties_add_editable_list(props, S_SIDECHAIN_SOURCE, - TEXT_SIDECHAIN_SOURCE, - OBS_EDITABLE_LIST_TYPE_SOURCES, "", - ""); + obs_properties_add_editable_list(props, S_SIDECHAIN_SOURCE, + TEXT_SIDECHAIN_SOURCE, + OBS_EDITABLE_LIST_TYPE_SOURCES, "", + ""); return props; } diff --git a/shared/properties-view/properties-view.cpp b/shared/properties-view/properties-view.cpp index d306fc99d..36cc8b64c 100644 --- a/shared/properties-view/properties-view.cpp +++ b/shared/properties-view/properties-view.cpp @@ -23,6 +23,7 @@ #include #include #include +#include #include #include "QtCore/qstring.h" #include "QtGui/qaction.h" @@ -2264,45 +2265,45 @@ public: }; class DropdownDialog : public QDialog { - QComboBox *dropdown; + QComboBox *dropdown; public: - DropdownDialog(QWidget *parent) : QDialog(parent) - { - QHBoxLayout *topLayout = new QHBoxLayout(); - QVBoxLayout *mainLayout = new QVBoxLayout(); + DropdownDialog(QWidget *parent) : QDialog(parent) + { + QHBoxLayout *topLayout = new QHBoxLayout(); + QVBoxLayout *mainLayout = new QVBoxLayout(); - dropdown = new QComboBox(); - topLayout->addWidget(dropdown); - topLayout->setAlignment(dropdown, Qt::AlignVCenter); + dropdown = new QComboBox(); + topLayout->addWidget(dropdown); + topLayout->setAlignment(dropdown, Qt::AlignVCenter); - QDialogButtonBox::StandardButtons buttons = - QDialogButtonBox::Ok | QDialogButtonBox::Cancel; + QDialogButtonBox::StandardButtons buttons = + QDialogButtonBox::Ok | QDialogButtonBox::Cancel; - QDialogButtonBox *buttonBox = new QDialogButtonBox(buttons); - buttonBox->setCenterButtons(true); + QDialogButtonBox *buttonBox = new QDialogButtonBox(buttons); + buttonBox->setCenterButtons(true); - mainLayout->addLayout(topLayout); - mainLayout->addWidget(buttonBox); + mainLayout->addLayout(topLayout); + mainLayout->addWidget(buttonBox); - setLayout(mainLayout); - resize(400, 80); + setLayout(mainLayout); + resize(400, 80); - connect(buttonBox, &QDialogButtonBox::accepted, this, - &DropdownDialog::accept); - connect(buttonBox, &QDialogButtonBox::rejected, this, - &DropdownDialog::reject); - } + connect(buttonBox, &QDialogButtonBox::accepted, this, + &DropdownDialog::accept); + connect(buttonBox, &QDialogButtonBox::rejected, this, + &DropdownDialog::reject); + } - int GetSelectedIndex() const { return dropdown->currentIndex(); } + int GetSelectedIndex() const { return dropdown->currentIndex(); } - QString GetSelectedString() { return dropdown->currentText(); } + QString GetSelectedString() { return dropdown->currentText(); } - void AddField(const char *name) { dropdown->addItem(name); } - void AddField(const QString &icon, const char *name) - { - dropdown->addItem(icon, name); - } + void AddField(const char *name) { dropdown->addItem(name); } + void AddField(const QString &icon, const char *name) + { + dropdown->addItem(icon, name); + } }; void WidgetInfo::EditListAdd() @@ -2314,11 +2315,11 @@ void WidgetInfo::EditListAdd() EditListAddText(); return; } - - if (type == OBS_EDITABLE_LIST_TYPE_SOURCES) { - EditListAddSource(); - return; - } + + if (type == OBS_EDITABLE_LIST_TYPE_SOURCES) { + EditListAddSource(); + return; + } /* Files and URLs */ QMenu popup(view->window()); @@ -2346,36 +2347,36 @@ void WidgetInfo::EditListAdd() void WidgetInfo::EditListAddSource() { - QListWidget *list = reinterpret_cast(widget); - const char *desc = obs_property_description(property); + QListWidget *list = reinterpret_cast(widget); + const char *desc = obs_property_description(property); - DropdownDialog dialog(widget->window()); - /* ListEntry reused because its syntactically correct here. */ - auto title = QTStr("Basic.PropertiesWindow.AddEditableListEntry") - .arg(QT_UTF8(desc)); + DropdownDialog dialog(widget->window()); + /* ListEntry reused because its syntactically correct here. */ + auto title = tr("Basic.PropertiesWindow.AddEditableListEntry") + .arg(QT_UTF8(desc)); - dialog.setWindowTitle(title); + dialog.setWindowTitle(title); - dialog.AddField(QTStr("None"), "none"); + dialog.AddField(tr("None"), "none"); - obs_enum_sources( - [](void *info, obs_source_t *source) { - auto dialog = reinterpret_cast(info); - const char *name = obs_source_get_name(source); + obs_enum_sources( + [](void *info, obs_source_t *source) { + auto dialog = reinterpret_cast(info); + const char *name = obs_source_get_name(source); - dialog->AddField(name); - return true; - }, - &dialog); + dialog->AddField(name); + return true; + }, + &dialog); - if (dialog.exec() == QDialog::Rejected) { - return; - } + if (dialog.exec() == QDialog::Rejected) { + return; + } - QString qstr = dialog.GetSelectedString(); + QString qstr = dialog.GetSelectedString(); - list->addItem(qstr); - EditableListChanged(); + list->addItem(qstr); + EditableListChanged(); } void WidgetInfo::EditListAddText() diff --git a/shared/properties-view/properties-view.hpp b/shared/properties-view/properties-view.hpp index 52b6326cd..3f7c4916b 100644 --- a/shared/properties-view/properties-view.hpp +++ b/shared/properties-view/properties-view.hpp @@ -74,7 +74,7 @@ public slots: void EditListAdd(); void EditListAddText(); void EditListAddFiles(); - void EditListAddSource(); + void EditListAddSource(); void EditListAddDir(); void EditListRemove(); void EditListEdit();