connect(ui->locut_cutoff_knob, &QDial::valueChanged, this, &MainWindow::cutoff_knob_changed);
cutoff_knob_changed(ui->locut_cutoff_knob->value());
+ // Not QDial::valueChanged, as we call setValue() all the time.
+ connect(ui->gainstaging_knob, &QAbstractSlider::sliderMoved, this, &MainWindow::gain_staging_knob_changed);
connect(ui->gainstaging_auto_checkbox, &QCheckBox::stateChanged, [this](int state){
- global_mixer->set_gainstaging_auto(state == Qt::Checked);
+ global_mixer->set_gain_staging_auto(state == Qt::Checked);
});
connect(ui->limiter_threshold_knob, &QDial::valueChanged, this, &MainWindow::limiter_threshold_knob_changed);
close();
}
+void MainWindow::gain_staging_knob_changed(int value)
+{
+ ui->gainstaging_auto_checkbox->setCheckState(Qt::Unchecked);
+
+ float gain_db = value * 0.1f;
+ global_mixer->set_gain_staging_db(gain_db);
+
+ // The label will be updated by the audio level callback.
+}
+
void MainWindow::cutoff_knob_changed(int value)
{
float octaves = value * 0.1f;
ui->peak_display->setStyleSheet("");
}
-void MainWindow::audio_level_callback(float level_lufs, float peak_db, float global_level_lufs, float range_low_lufs, float range_high_lufs, float auto_gain_staging_db)
+void MainWindow::audio_level_callback(float level_lufs, float peak_db, float global_level_lufs, float range_low_lufs, float range_high_lufs, float gain_staging_db)
{
timeval now;
gettimeofday(&now, nullptr);
ui->peak_display->setStyleSheet("");
}
- ui->gainstaging_knob->setValue(lrintf(auto_gain_staging_db * 10.0f));
- snprintf(buf, sizeof(buf), "%+.1f dB", auto_gain_staging_db);
+ ui->gainstaging_knob->setValue(lrintf(gain_staging_db * 10.0f));
+ snprintf(buf, sizeof(buf), "%+.1f dB", gain_staging_db);
ui->gainstaging_db_display->setText(buf);
});
}
void wb_button_clicked(int channel_number);
void set_transition_names(std::vector<std::string> transition_names);
void update_channel_name(Mixer::Output output);
+ void gain_staging_knob_changed(int value);
void cutoff_knob_changed(int value);
void limiter_threshold_knob_changed(int value);
void compressor_threshold_knob_changed(int value);
}
if (audio_level_callback != nullptr) {
- unique_lock<mutex> lock(r128_mutex);
+ unique_lock<mutex> lock(compressor_mutex);
double loudness_s = r128.loudness_S();
double loudness_i = r128.integrated();
double loudness_range_low = r128.range_min();
audio_level_callback(loudness_s, 20.0 * log10(peak),
loudness_i, loudness_range_low, loudness_range_high,
- last_gain_staging_db);
+ gain_staging_db);
}
for (unsigned card_index = 1; card_index < num_cards; ++card_index) {
// then apply a makeup gain to get it to -14 dBFS. -14 dBFS is, of course,
// entirely arbitrary, but from practical tests with speech, it seems to
// put ut around -23 LUFS, so it's a reasonable starting point for later use.
- if (level_compressor_enabled) {
- float threshold = 0.01f; // -40 dBFS.
- float ratio = 20.0f;
- float attack_time = 0.5f;
- float release_time = 20.0f;
- float makeup_gain = pow(10.0f, (ref_level_dbfs - (-40.0f)) / 20.0f); // +26 dB.
- level_compressor.process(samples_out.data(), samples_out.size() / 2, threshold, ratio, attack_time, release_time, makeup_gain);
- last_gain_staging_db = 20.0 * log10(level_compressor.get_attenuation() * makeup_gain);
+ {
+ unique_lock<mutex> lock(level_compressor_mutex);
+ if (level_compressor_enabled) {
+ float threshold = 0.01f; // -40 dBFS.
+ float ratio = 20.0f;
+ float attack_time = 0.5f;
+ float release_time = 20.0f;
+ float makeup_gain = pow(10.0f, (ref_level_dbfs - (-40.0f)) / 20.0f); // +26 dB.
+ level_compressor.process(samples_out.data(), samples_out.size() / 2, threshold, ratio, attack_time, release_time, makeup_gain);
+ gain_staging_db = 20.0 * log10(level_compressor.get_attenuation() * makeup_gain);
+ } else {
+ // Just apply the gain we already had.
+ float g = pow(10.0f, gain_staging_db / 20.0f);
+ for (size_t i = 0; i < samples_out.size(); ++i) {
+ samples_out[i] *= g;
+ }
+ }
}
#if 0
deinterleave_samples(samples_out, &left, &right);
float *ptrs[] = { left.data(), right.data() };
{
- unique_lock<mutex> lock(r128_mutex);
+ unique_lock<mutex> lock(compressor_mutex);
r128.process(left.size(), ptrs);
}
typedef std::function<void(float level_lufs, float peak_db,
float global_level_lufs, float range_low_lufs, float range_high_lufs,
- float auto_gain_staging_db)> audio_level_callback_t;
+ float gain_staging_db)> audio_level_callback_t;
void set_audio_level_callback(audio_level_callback_t callback)
{
audio_level_callback = callback;
compressor_enabled = enabled;
}
- void set_gainstaging_auto(bool enabled)
+ void set_gain_staging_db(float gain_db)
{
- level_compressor = enabled;
+ std::unique_lock<std::mutex> lock(level_compressor_mutex);
+ level_compressor_enabled = false;
+ gain_staging_db = gain_db;
+ }
+
+ void set_gain_staging_auto(bool enabled)
+ {
+ std::unique_lock<std::mutex> lock(level_compressor_mutex);
+ level_compressor_enabled = enabled;
}
void schedule_cut()
std::atomic<bool> should_cut{false};
audio_level_callback_t audio_level_callback = nullptr;
- std::mutex r128_mutex;
- Ebu_r128_proc r128; // Under r128_mutex.
+ std::mutex compressor_mutex;
+ Ebu_r128_proc r128; // Under compressor_mutex.
Resampler peak_resampler;
std::atomic<float> peak{0.0f};
std::atomic<float> locut_cutoff_hz;
// First compressor; takes us up to about -12 dBFS.
- StereoCompressor level_compressor;
- float last_gain_staging_db = 0.0f;
- std::atomic<bool> level_compressor_enabled{true};
+ std::mutex level_compressor_mutex;
+ StereoCompressor level_compressor; // Under compressor_mutex. Used to set/override gain_staging_db if <level_compressor_enabled>.
+ float gain_staging_db = 0.0f; // Under compressor_mutex.
+ bool level_compressor_enabled = true; // Under compressor_mutex.
static constexpr float ref_level_dbfs = -14.0f;