X-Git-Url: https://git.sesse.net/?a=blobdiff_plain;f=mainwindow.cpp;h=0eda02d3a5c82bcdc8d8763971ccf225faa393aa;hb=402b3d36c13bb04e9a2f690c5c12d8f85d6dc415;hp=9e4ad0d9e5dc4b6fa48036d6ea848bb51603145d;hpb=e5b9eff03d96d79c93a33d354b51c1afe2f44d59;p=nageru diff --git a/mainwindow.cpp b/mainwindow.cpp index 9e4ad0d..0eda02d 100644 --- a/mainwindow.cpp +++ b/mainwindow.cpp @@ -1,40 +1,70 @@ #include "mainwindow.h" -#include -#include +#include #include -#include -#include -#include -#include +#include +#include +#include +#include +#include +#include +#include +#include #include +#include +#include +#include +#include +#include +#include +#include #include #include #include +#include +#include #include -#include +#include +#include #include -#include +#include +#include #include -#include -#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include #include "aboutdialog.h" +#include "alsa_pool.h" +#include "clickable_label.h" +#include "correlation_meter.h" #include "disk_space_estimator.h" +#include "ellipsis_label.h" #include "flags.h" #include "glwidget.h" +#include "input_mapping.h" #include "input_mapping_dialog.h" #include "lrameter.h" +#include "midi_mapping.pb.h" +#include "midi_mapping_dialog.h" #include "mixer.h" +#include "nonlinear_fader.h" #include "post_to_main_thread.h" -#include "ui_audio_miniview.h" #include "ui_audio_expanded_view.h" +#include "ui_audio_miniview.h" #include "ui_display.h" #include "ui_mainwindow.h" #include "vumeter.h" -class QResizeEvent; - using namespace std; using namespace std::chrono; using namespace std::placeholders; @@ -44,6 +74,10 @@ Q_DECLARE_METATYPE(std::vector); MainWindow *global_mainwindow = nullptr; +// -0.1 dBFS is EBU peak limit. We use it consistently, even for the bus meters +// (which don't calculate interpolate peak, and in general don't follow EBU recommendations). +constexpr float peak_limit_dbfs = -0.1f; + namespace { void schedule_cut_signal(int ignored) @@ -132,9 +166,7 @@ void set_peak_label(QLabel *peak_label, float peak_db) { peak_label->setText(QString::fromStdString(format_db(peak_db, DB_BARE))); - // -0.1 dBFS is EBU peak limit. We use it consistently, even for the bus meters - // (which don't calculate interpolate peak, and in general don't follow EBU recommendations). - if (peak_db > -0.1f) { + if (peak_db > peak_limit_dbfs) { peak_label->setStyleSheet("QLabel { background-color: red; color: white; }"); } else { peak_label->setStyleSheet(""); @@ -144,7 +176,7 @@ void set_peak_label(QLabel *peak_label, float peak_db) } // namespace MainWindow::MainWindow() - : ui(new Ui::MainWindow) + : ui(new Ui::MainWindow), midi_mapper(this) { global_mainwindow = this; ui->setupUi(this); @@ -168,6 +200,7 @@ MainWindow::MainWindow() connect(ui->simple_audio_mode, &QAction::triggered, this, &MainWindow::simple_audio_mode_triggered); connect(ui->multichannel_audio_mode, &QAction::triggered, this, &MainWindow::multichannel_audio_mode_triggered); connect(ui->input_mapping_action, &QAction::triggered, this, &MainWindow::input_mapping_triggered); + connect(ui->midi_mapping_action, &QAction::triggered, this, &MainWindow::midi_mapping_triggered); if (global_flags.x264_video_to_http) { connect(ui->x264_bitrate_action, &QAction::triggered, this, &MainWindow::x264_bitrate_triggered); @@ -206,6 +239,19 @@ MainWindow::MainWindow() connect(new QShortcut(QKeySequence::MoveToPreviousPage, this), &QShortcut::activated, switch_page); last_audio_level_callback = steady_clock::now() - seconds(1); + + if (!global_flags.midi_mapping_filename.empty()) { + MIDIMappingProto midi_mapping; + if (!load_midi_mapping_from_file(global_flags.midi_mapping_filename, &midi_mapping)) { + fprintf(stderr, "Couldn't load MIDI mapping '%s'; exiting.\n", + global_flags.midi_mapping_filename.c_str()); + exit(1); + } + midi_mapper.set_midi_mapping(midi_mapping); + } + midi_mapper.refresh_highlights(); + midi_mapper.refresh_lights(); + midi_mapper.start_thread(); } void MainWindow::resizeEvent(QResizeEvent* event) @@ -276,16 +322,19 @@ void MainWindow::mixer_created(Mixer *mixer) } connect(ui->locut_enabled, &QCheckBox::stateChanged, [this](int state){ global_audio_mixer->set_locut_enabled(simple_bus_index, state == Qt::Checked); + midi_mapper.refresh_lights(); }); connect(ui->gainstaging_knob, &QAbstractSlider::valueChanged, bind(&MainWindow::gain_staging_knob_changed, this, simple_bus_index, _1)); connect(ui->gainstaging_auto_checkbox, &QCheckBox::stateChanged, [this, simple_bus_index](int state){ global_audio_mixer->set_gain_staging_auto(simple_bus_index, state == Qt::Checked); + midi_mapper.refresh_lights(); }); connect(ui->compressor_threshold_knob, &QDial::valueChanged, bind(&MainWindow::compressor_threshold_knob_changed, this, simple_bus_index, _1)); connect(ui->compressor_enabled, &QCheckBox::stateChanged, [this, simple_bus_index](int state){ global_audio_mixer->set_compressor_enabled(simple_bus_index, state == Qt::Checked); + midi_mapper.refresh_lights(); }); // Global mastering controls. @@ -300,15 +349,24 @@ void MainWindow::mixer_created(Mixer *mixer) connect(ui->makeup_gain_knob, &QAbstractSlider::valueChanged, this, &MainWindow::final_makeup_gain_knob_changed); connect(ui->makeup_gain_auto_checkbox, &QCheckBox::stateChanged, [this](int state){ global_audio_mixer->set_final_makeup_gain_auto(state == Qt::Checked); + midi_mapper.refresh_lights(); }); connect(ui->limiter_threshold_knob, &QDial::valueChanged, this, &MainWindow::limiter_threshold_knob_changed); connect(ui->limiter_enabled, &QCheckBox::stateChanged, [this](int state){ global_audio_mixer->set_limiter_enabled(state == Qt::Checked); + midi_mapper.refresh_lights(); }); connect(ui->reset_meters_button, &QPushButton::clicked, this, &MainWindow::reset_meters_button_clicked); + // Even though we have a reset button right next to it, the fact that + // the expanded audio view labels are clickable makes it natural to + // click this one as well. + connect(ui->peak_display, &ClickableLabel::clicked, this, &MainWindow::reset_meters_button_clicked); mixer->get_audio_mixer()->set_audio_level_callback(bind(&MainWindow::audio_level_callback, this, _1, _2, _3, _4, _5, _6, _7, _8)); + midi_mapper.refresh_highlights(); + midi_mapper.refresh_lights(); + struct sigaction act; memset(&act, 0, sizeof(act)); act.sa_handler = schedule_cut_signal; @@ -330,6 +388,7 @@ void MainWindow::reset_audio_mapping_ui() ui->simple_audio_mode->setChecked(simple); ui->multichannel_audio_mode->setChecked(!simple); ui->input_mapping_action->setEnabled(!simple); + ui->midi_mapping_action->setEnabled(!simple); ui->locut_enabled->setVisible(simple); ui->gainstaging_label->setVisible(simple); @@ -348,6 +407,9 @@ void MainWindow::reset_audio_mapping_ui() ui->audio_views->setCurrentIndex(0); } ui->compact_header->setVisible(!simple); + + midi_mapper.refresh_highlights(); + midi_mapper.refresh_lights(); } void MainWindow::setup_audio_miniview() @@ -420,11 +482,15 @@ void MainWindow::setup_audio_expanded_view() update_eq_label(bus_index, EQ_BAND_MID, global_audio_mixer->get_eq(bus_index, EQ_BAND_MID)); update_eq_label(bus_index, EQ_BAND_BASS, global_audio_mixer->get_eq(bus_index, EQ_BAND_BASS)); ui_audio_expanded_view->fader->setDbValue(global_audio_mixer->get_fader_volume(bus_index)); + ui_audio_expanded_view->mute_button->setChecked(global_audio_mixer->get_mute(bus_index) ? Qt::Checked : Qt::Unchecked); + connect(ui_audio_expanded_view->mute_button, &QPushButton::toggled, + bind(&MainWindow::mute_button_toggled, this, bus_index, _1)); ui->buses->addWidget(channel); ui_audio_expanded_view->locut_enabled->setChecked(global_audio_mixer->get_locut_enabled(bus_index)); connect(ui_audio_expanded_view->locut_enabled, &QCheckBox::stateChanged, [this, bus_index](int state){ global_audio_mixer->set_locut_enabled(bus_index, state == Qt::Checked); + midi_mapper.refresh_lights(); }); connect(ui_audio_expanded_view->treble_knob, &QDial::valueChanged, @@ -441,11 +507,13 @@ void MainWindow::setup_audio_expanded_view() connect(ui_audio_expanded_view->gainstaging_knob, &QAbstractSlider::valueChanged, bind(&MainWindow::gain_staging_knob_changed, this, bus_index, _1)); connect(ui_audio_expanded_view->gainstaging_auto_checkbox, &QCheckBox::stateChanged, [this, bus_index](int state){ global_audio_mixer->set_gain_staging_auto(bus_index, state == Qt::Checked); + midi_mapper.refresh_lights(); }); connect(ui_audio_expanded_view->compressor_threshold_knob, &QDial::valueChanged, bind(&MainWindow::compressor_threshold_knob_changed, this, bus_index, _1)); connect(ui_audio_expanded_view->compressor_enabled, &QCheckBox::stateChanged, [this, bus_index](int state){ global_audio_mixer->set_compressor_enabled(bus_index, state == Qt::Checked); + midi_mapper.refresh_lights(); }); slave_fader(audio_miniviews[bus_index]->fader, ui_audio_expanded_view->fader); @@ -457,8 +525,9 @@ void MainWindow::setup_audio_expanded_view() peak_meter->set_ref_level(0.0f); connect(ui_audio_expanded_view->peak_display_label, &ClickableLabel::clicked, - [bus_index]() { + [this, bus_index]() { global_audio_mixer->reset_peak(bus_index); + midi_mapper.refresh_lights(); }); // Set up the compression attenuation meter. @@ -549,6 +618,13 @@ void MainWindow::input_mapping_triggered() setup_audio_miniview(); setup_audio_expanded_view(); } + midi_mapper.refresh_highlights(); + midi_mapper.refresh_lights(); +} + +void MainWindow::midi_mapping_triggered() +{ + MIDIMappingDialog(&midi_mapper).exec(); } void MainWindow::gain_staging_knob_changed(unsigned bus_index, int value) @@ -690,6 +766,12 @@ void MainWindow::mini_fader_changed(int bus, double volume_db) global_audio_mixer->set_fader_volume(bus, volume_db); } +void MainWindow::mute_button_toggled(int bus, bool checked) +{ + global_audio_mixer->set_mute(bus, checked); + midi_mapper.refresh_lights(); +} + void MainWindow::reset_meters_button_clicked() { global_audio_mixer->reset_meters(); @@ -743,6 +825,8 @@ void MainWindow::audio_level_callback(float level_lufs, float peak_db, vectorpeak_display_label, level.historic_peak_dbfs); + + midi_mapper.set_has_peaked(bus_index, level.historic_peak_dbfs >= -0.1f); } } ui->lra_meter->set_levels(global_level_lufs, range_low_lufs, range_high_lufs); @@ -765,6 +849,9 @@ void MainWindow::audio_level_callback(float level_lufs, float peak_db, vectormakeup_gain_db_display_2->setText( QString::fromStdString(format_db(final_makeup_gain_db, DB_WITH_SIGN))); + + // Peak labels could have changed. + midi_mapper.refresh_lights(); }); } @@ -821,6 +908,287 @@ void MainWindow::relayout() ui->preview_displays->setStretch(previews.size(), lrintf(remaining_preview_width)); } +void MainWindow::set_locut(float value) +{ + set_relative_value(ui->locut_cutoff_knob, value); +} + +void MainWindow::set_limiter_threshold(float value) +{ + set_relative_value(ui->limiter_threshold_knob, value); +} + +void MainWindow::set_makeup_gain(float value) +{ + set_relative_value(ui->makeup_gain_knob, value); +} + +void MainWindow::set_treble(unsigned bus_idx, float value) +{ + set_relative_value_if_exists(bus_idx, &Ui::AudioExpandedView::treble_knob, value); +} + +void MainWindow::set_mid(unsigned bus_idx, float value) +{ + set_relative_value_if_exists(bus_idx, &Ui::AudioExpandedView::mid_knob, value); +} + +void MainWindow::set_bass(unsigned bus_idx, float value) +{ + set_relative_value_if_exists(bus_idx, &Ui::AudioExpandedView::bass_knob, value); +} + +void MainWindow::set_gain(unsigned bus_idx, float value) +{ + set_relative_value_if_exists(bus_idx, &Ui::AudioExpandedView::gainstaging_knob, value); +} + +void MainWindow::set_compressor_threshold(unsigned bus_idx, float value) +{ + set_relative_value_if_exists(bus_idx, &Ui::AudioExpandedView::compressor_threshold_knob, value); +} + +void MainWindow::set_fader(unsigned bus_idx, float value) +{ + set_relative_value_if_exists(bus_idx, &Ui::AudioExpandedView::fader, value); +} + +void MainWindow::toggle_mute(unsigned bus_idx) +{ + click_button_if_exists(bus_idx, &Ui::AudioExpandedView::mute_button); +} + +void MainWindow::toggle_locut(unsigned bus_idx) +{ + click_button_if_exists(bus_idx, &Ui::AudioExpandedView::locut_enabled); +} + +void MainWindow::toggle_auto_gain_staging(unsigned bus_idx) +{ + click_button_if_exists(bus_idx, &Ui::AudioExpandedView::gainstaging_auto_checkbox); +} + +void MainWindow::toggle_compressor(unsigned bus_idx) +{ + click_button_if_exists(bus_idx, &Ui::AudioExpandedView::compressor_enabled); +} + +void MainWindow::clear_peak(unsigned bus_idx) +{ + post_to_main_thread([=]{ + if (global_audio_mixer->get_mapping_mode() == AudioMixer::MappingMode::MULTICHANNEL) { + global_audio_mixer->reset_peak(bus_idx); + midi_mapper.set_has_peaked(bus_idx, false); + midi_mapper.refresh_lights(); + } + }); +} + +void MainWindow::clear_all_highlights() +{ + post_to_main_thread([this]{ + highlight_locut(false); + highlight_limiter_threshold(false); + highlight_makeup_gain(false); + highlight_toggle_limiter(false); + highlight_toggle_auto_makeup_gain(false); + for (unsigned bus_idx = 0; bus_idx < audio_expanded_views.size(); ++bus_idx) { + highlight_treble(bus_idx, false); + highlight_mid(bus_idx, false); + highlight_bass(bus_idx, false); + highlight_gain(bus_idx, false); + highlight_compressor_threshold(bus_idx, false); + highlight_fader(bus_idx, false); + highlight_mute(bus_idx, false); + highlight_toggle_locut(bus_idx, false); + highlight_toggle_auto_gain_staging(bus_idx, false); + highlight_toggle_compressor(bus_idx, false); + } + }); +} + +void MainWindow::toggle_limiter() +{ + if (global_audio_mixer->get_mapping_mode() == AudioMixer::MappingMode::MULTICHANNEL) { + ui->limiter_enabled->click(); + } +} + +void MainWindow::toggle_auto_makeup_gain() +{ + if (global_audio_mixer->get_mapping_mode() == AudioMixer::MappingMode::MULTICHANNEL) { + ui->makeup_gain_auto_checkbox->click(); + } +} + +void MainWindow::highlight_locut(bool highlight) +{ + post_to_main_thread([this, highlight]{ + highlight_control(ui->locut_cutoff_knob, highlight); + highlight_control(ui->locut_cutoff_knob_2, highlight); + }); +} + +void MainWindow::highlight_limiter_threshold(bool highlight) +{ + post_to_main_thread([this, highlight]{ + highlight_control(ui->limiter_threshold_knob, highlight); + highlight_control(ui->limiter_threshold_knob_2, highlight); + }); +} + +void MainWindow::highlight_makeup_gain(bool highlight) +{ + post_to_main_thread([this, highlight]{ + highlight_control(ui->makeup_gain_knob, highlight); + highlight_control(ui->makeup_gain_knob_2, highlight); + }); +} + +void MainWindow::highlight_treble(unsigned bus_idx, bool highlight) +{ + highlight_control_if_exists(bus_idx, &Ui::AudioExpandedView::treble_knob, highlight); +} + +void MainWindow::highlight_mid(unsigned bus_idx, bool highlight) +{ + highlight_control_if_exists(bus_idx, &Ui::AudioExpandedView::mid_knob, highlight); +} + +void MainWindow::highlight_bass(unsigned bus_idx, bool highlight) +{ + highlight_control_if_exists(bus_idx, &Ui::AudioExpandedView::bass_knob, highlight); +} + +void MainWindow::highlight_gain(unsigned bus_idx, bool highlight) +{ + highlight_control_if_exists(bus_idx, &Ui::AudioExpandedView::gainstaging_knob, highlight); +} + +void MainWindow::highlight_compressor_threshold(unsigned bus_idx, bool highlight) +{ + highlight_control_if_exists(bus_idx, &Ui::AudioExpandedView::compressor_threshold_knob, highlight); +} + +void MainWindow::highlight_fader(unsigned bus_idx, bool highlight) +{ + highlight_control_if_exists(bus_idx, &Ui::AudioExpandedView::fader, highlight); +} + +void MainWindow::highlight_mute(unsigned bus_idx, bool highlight) +{ + highlight_control_if_exists(bus_idx, &Ui::AudioExpandedView::mute_button, highlight, /*is_mute_btton=*/true); +} + +void MainWindow::highlight_toggle_locut(unsigned bus_idx, bool highlight) +{ + highlight_control_if_exists(bus_idx, &Ui::AudioExpandedView::locut_enabled, highlight); +} + +void MainWindow::highlight_toggle_auto_gain_staging(unsigned bus_idx, bool highlight) +{ + highlight_control_if_exists(bus_idx, &Ui::AudioExpandedView::gainstaging_auto_checkbox, highlight); +} + +void MainWindow::highlight_toggle_compressor(unsigned bus_idx, bool highlight) +{ + highlight_control_if_exists(bus_idx, &Ui::AudioExpandedView::compressor_enabled, highlight); +} + +void MainWindow::highlight_toggle_limiter(bool highlight) +{ + post_to_main_thread([this, highlight]{ + highlight_control(ui->limiter_enabled, highlight); + highlight_control(ui->limiter_enabled_2, highlight); + }); +} + +void MainWindow::highlight_toggle_auto_makeup_gain(bool highlight) +{ + post_to_main_thread([this, highlight]{ + highlight_control(ui->makeup_gain_auto_checkbox, highlight); + highlight_control(ui->makeup_gain_auto_checkbox_2, highlight); + }); +} + +template +void MainWindow::set_relative_value(T *control, float value) +{ + post_to_main_thread([control, value]{ + control->setValue(lrintf(control->minimum() + value * (control->maximum() - control->minimum()))); + }); +} + +template +void MainWindow::set_relative_value_if_exists(unsigned bus_idx, T *(Ui_AudioExpandedView::*control), float value) +{ + if (global_audio_mixer != nullptr && + global_audio_mixer->get_mapping_mode() == AudioMixer::MappingMode::MULTICHANNEL && + bus_idx < audio_expanded_views.size()) { + set_relative_value(audio_expanded_views[bus_idx]->*control, value); + } +} + +template +void MainWindow::click_button_if_exists(unsigned bus_idx, T *(Ui_AudioExpandedView::*control)) +{ + post_to_main_thread([this, bus_idx, control]{ + if (global_audio_mixer != nullptr && + global_audio_mixer->get_mapping_mode() == AudioMixer::MappingMode::MULTICHANNEL && + bus_idx < audio_expanded_views.size()) { + (audio_expanded_views[bus_idx]->*control)->click(); + } + }); +} + +template +void MainWindow::highlight_control(T *control, bool highlight) +{ + if (control == nullptr) { + return; + } + if (global_audio_mixer == nullptr || + global_audio_mixer->get_mapping_mode() != AudioMixer::MappingMode::MULTICHANNEL) { + highlight = false; + } + if (highlight) { + control->setStyleSheet("background: rgb(0,255,0,80)"); + } else { + control->setStyleSheet(""); + } +} + +template +void MainWindow::highlight_mute_control(T *control, bool highlight) +{ + if (control == nullptr) { + return; + } + if (global_audio_mixer == nullptr || + global_audio_mixer->get_mapping_mode() != AudioMixer::MappingMode::MULTICHANNEL) { + highlight = false; + } + if (highlight) { + control->setStyleSheet("QPushButton { background: rgb(0,255,0,80); } QPushButton:checked { background: rgba(255,80,0,140); }"); + } else { + control->setStyleSheet("QPushButton:checked { background: rgba(255,0,0,80); }"); + } +} + +template +void MainWindow::highlight_control_if_exists(unsigned bus_idx, T *(Ui_AudioExpandedView::*control), bool highlight, bool is_mute_button) +{ + post_to_main_thread([this, bus_idx, control, highlight, is_mute_button]{ + if (bus_idx < audio_expanded_views.size()) { + if (is_mute_button) { + highlight_mute_control(audio_expanded_views[bus_idx]->*control, highlight); + } else { + highlight_control(audio_expanded_views[bus_idx]->*control, highlight); + } + } + }); +} + void MainWindow::set_transition_names(vector transition_names) { if (transition_names.size() < 1 || transition_names[0].empty()) {