]> git.sesse.net Git - nageru/commitdiff
Update the UI with state changes and new ALSA cards as they come in.
authorSteinar H. Gunderson <sgunderson@bigfoot.com>
Sun, 11 Sep 2016 15:34:08 +0000 (17:34 +0200)
committerSteinar H. Gunderson <sgunderson@bigfoot.com>
Wed, 19 Oct 2016 22:55:44 +0000 (00:55 +0200)
alsa_input.cpp
alsa_input.h
audio_mixer.h
input_mapping_dialog.cpp
input_mapping_dialog.h
mainwindow.cpp
mainwindow.h
mixer.cpp

index 0ba07b145dc7a0fcc4f198fec772660d1f6088b8..09cbbd2bc66526fb6be6544ac431774ec444968f 100644 (file)
@@ -467,6 +467,9 @@ ALSAPool::ProbeResult ALSAPool::probe_device_once(unsigned card_index, unsigned
        fprintf(stderr, "%s: Probed successfully.\n", address);
 
        reset_device(internal_dev_index);  // Restarts it if it is held (ie., we just replaced a dead card).
+       if (global_audio_mixer) {
+               global_audio_mixer->trigger_state_changed_callback();
+       }
 
        return ALSAPool::ProbeResult::SUCCESS;
 }
@@ -589,6 +592,7 @@ void ALSAPool::set_card_state(unsigned index, ALSAPool::Device::State state)
        bool silence = (state != ALSAPool::Device::State::RUNNING);
        while (!global_audio_mixer->silence_card(spec, silence))
                ;
+       global_audio_mixer->trigger_state_changed_callback();
 }
 
 unsigned ALSAPool::find_free_device_index(const string &name, const string &info, unsigned num_channels, const string &address)
@@ -643,15 +647,19 @@ void ALSAPool::free_card(unsigned index)
        while (!global_audio_mixer->silence_card(spec, true))
                ;
 
-       lock_guard<mutex> lock(mu);
-       if (devices[index].held) {
-               devices[index].state = Device::State::DEAD;
-       } else {
-               devices[index].state = Device::State::EMPTY;
-               inputs[index].reset();
-       }
-       while (!devices.empty() && devices.back().state == Device::State::EMPTY) {
-               devices.pop_back();
-               inputs.pop_back();
+       {
+               lock_guard<mutex> lock(mu);
+               if (devices[index].held) {
+                       devices[index].state = Device::State::DEAD;
+               } else {
+                       devices[index].state = Device::State::EMPTY;
+                       inputs[index].reset();
+               }
+               while (!devices.empty() && devices.back().state == Device::State::EMPTY) {
+                       devices.pop_back();
+                       inputs.pop_back();
+               }
        }
+
+       global_audio_mixer->trigger_state_changed_callback();
 }
index c0dbafc4e69e408c55538e3dc5446d51ef6a73c4..f1216dea5a3aa898cfcca00f304eef0b24446504 100644 (file)
@@ -150,9 +150,6 @@ public:
        // EMPTY or DEAD state. Only for ALSAInput and for internal use.
        void free_card(unsigned index);
 
-       // TODO: Add accessors and/or callbacks about changed state, so that
-       // the UI actually stands a chance in using that information.
-
 private:
        mutable std::mutex mu;
        std::vector<Device> devices;  // Under mu.
index 855295dcd60f0bf207f435c75484a763eba77b89..2e73ebd7a1e7e68ff29dee3024dbc90b8911b316 100644 (file)
@@ -262,6 +262,24 @@ public:
                audio_level_callback = callback;
        }
 
+       typedef std::function<void()> state_changed_callback_t;
+       void set_state_changed_callback(state_changed_callback_t callback)
+       {
+               state_changed_callback = callback;
+       }
+
+       state_changed_callback_t get_state_changed_callback() const
+       {
+               return state_changed_callback;
+       }
+
+       void trigger_state_changed_callback()
+       {
+               if (state_changed_callback != nullptr) {
+                       state_changed_callback();
+               }
+       }
+
 private:
        struct AudioDevice {
                std::unique_ptr<ResamplingQueue> resampling_queue;
@@ -338,6 +356,7 @@ private:
        std::atomic<float> eq_level_db[MAX_BUSES][NUM_EQ_BANDS] {{{ 0.0f }}};
 
        audio_level_callback_t audio_level_callback = nullptr;
+       state_changed_callback_t state_changed_callback = nullptr;
        mutable std::mutex audio_measure_mutex;
        Ebu_r128_proc r128;  // Under audio_measure_mutex.
        CorrelationMeasurer correlation;  // Under audio_measure_mutex.
index 08d68dcbc05ed783f1dacb2bf3aa30c2a8ab3790..0496d95b068432256d65579e1dcffa8deb9717b3 100644 (file)
@@ -1,5 +1,6 @@
 #include "input_mapping_dialog.h"
 
+#include "post_to_main_thread.h"
 #include "ui_input_mapping.h"
 
 #include <QComboBox>
@@ -28,6 +29,21 @@ InputMappingDialog::InputMappingDialog()
 
        update_button_state();
        connect(ui->table, &QTableWidget::itemSelectionChanged, this, &InputMappingDialog::update_button_state);
+
+       saved_callback = global_audio_mixer->get_state_changed_callback();
+       global_audio_mixer->set_state_changed_callback([this]{
+               post_to_main_thread([this]{
+                       devices = global_audio_mixer->get_devices();
+                       for (unsigned row = 0; row < mapping.buses.size(); ++row) {
+                               fill_row_from_bus(row, mapping.buses[row]);
+                       }
+               });
+       });
+}
+
+InputMappingDialog::~InputMappingDialog()
+{
+       global_audio_mixer->set_state_changed_callback(saved_callback);
 }
 
 void InputMappingDialog::fill_ui_from_mapping(const InputMapping &mapping)
@@ -49,10 +65,17 @@ void InputMappingDialog::fill_row_from_bus(unsigned row, const InputMapping::Bus
        QString name(QString::fromStdString(bus.name));
        ui->table->setItem(row, 0, new QTableWidgetItem(name));
 
-       // Card choices.
-       QComboBox *card_combo = new QComboBox;
+       // Card choices. If there's already a combobox here, we try to modify
+       // the elements in-place, so that the UI doesn't go away under the user's feet
+       // if they are in the process of choosing an item.
+       QComboBox *card_combo = static_cast<QComboBox *>(ui->table->cellWidget(row, 1));
+       if (card_combo == nullptr) {
+               card_combo = new QComboBox;
+       }
        unsigned current_index = 0;
-       card_combo->addItem(QString("(none)   "));
+       if (card_combo->count() == 0) {
+               card_combo->addItem(QString("(none)   "));
+       }
        for (const auto &spec_and_info : devices) {
                QString label(QString::fromStdString(spec_and_info.second.name));
                if (spec_and_info.first.type == InputSourceType::ALSA_INPUT) {
@@ -66,13 +89,23 @@ void InputMappingDialog::fill_row_from_bus(unsigned row, const InputMapping::Bus
                        }
                }
                ++current_index;
-               card_combo->addItem(
-                       label + "   ",
-                       qulonglong(DeviceSpec_to_key(spec_and_info.first)));
+               if (unsigned(card_combo->count()) > current_index) {
+                       card_combo->setItemText(current_index, label + "   ");
+                       card_combo->setItemData(current_index, qulonglong(DeviceSpec_to_key(spec_and_info.first)));
+               } else {
+                       card_combo->addItem(
+                               label + "   ",
+                               qulonglong(DeviceSpec_to_key(spec_and_info.first)));
+               }
                if (bus.device == spec_and_info.first) {
                        card_combo->setCurrentIndex(current_index);
                }
        }
+       // Remove any excess items from earlier. (This is only for paranoia;
+       // they should be held, so it shouldn't matter.)
+       while (unsigned(card_combo->count()) > current_index + 1) {
+               card_combo->removeItem(current_index + 1);
+       }
        connect(card_combo, static_cast<void(QComboBox::*)(int)>(&QComboBox::currentIndexChanged),
                bind(&InputMappingDialog::card_selected, this, card_combo, row, _1));
        ui->table->setCellWidget(row, 1, card_combo);
@@ -83,6 +116,8 @@ void InputMappingDialog::fill_row_from_bus(unsigned row, const InputMapping::Bus
 void InputMappingDialog::setup_channel_choices_from_bus(unsigned row, const InputMapping::Bus &bus)
 {
        // Left and right channel.
+       // TODO: If there's already a widget here, modify it instead of creating a new one,
+       // as we do with card choices.
        for (unsigned channel = 0; channel < 2; ++channel) {
                QComboBox *channel_combo = new QComboBox;
                channel_combo->addItem(QString("(none)"));
@@ -108,12 +143,14 @@ void InputMappingDialog::setup_channel_choices_from_bus(unsigned row, const Inpu
 
 void InputMappingDialog::ok_clicked()
 {
+       global_audio_mixer->set_state_changed_callback(saved_callback);
        global_audio_mixer->set_input_mapping(mapping);
        accept();
 }
 
 void InputMappingDialog::cancel_clicked()
 {
+       global_audio_mixer->set_state_changed_callback(saved_callback);
        global_audio_mixer->set_input_mapping(old_mapping);
        reject();
 }
index d092a34cd5f12f71356a67f59956575de8e5ffca..0ca81d976866fbd3079015d5da9abdb0bc48e19c 100644 (file)
@@ -6,6 +6,7 @@
 #include <vector>
 #include <sys/time.h>
 
+#include "audio_mixer.h"
 #include "mixer.h"
 
 namespace Ui {
@@ -20,6 +21,7 @@ class InputMappingDialog : public QDialog
 
 public:
        InputMappingDialog();
+       ~InputMappingDialog();
 
 private:
        void fill_ui_from_mapping(const InputMapping &mapping);
@@ -43,7 +45,9 @@ private:
        // held forever).
        InputMapping old_mapping;
 
-       const std::map<DeviceSpec, DeviceInfo> devices;
+       std::map<DeviceSpec, DeviceInfo> devices;
+
+       AudioMixer::state_changed_callback_t saved_callback;
 };
 
 #endif  // !defined(_INPUT_MAPPING_DIALOG_H)
index a16d5bd159bfde3b37ac85833907737102a7754c..0fad15a785fe115f04c0f978bb3b002645bbaeae 100644 (file)
@@ -235,6 +235,7 @@ void MainWindow::mixer_created(Mixer *mixer)
 
        setup_audio_miniview();
        setup_audio_expanded_view();
+       global_audio_mixer->set_state_changed_callback(bind(&MainWindow::audio_state_changed, this));
 
        slave_knob(ui->locut_cutoff_knob, ui->locut_cutoff_knob_2);
        slave_knob(ui->limiter_threshold_knob, ui->limiter_threshold_knob_2);
@@ -841,3 +842,27 @@ void MainWindow::set_white_balance(int channel_number, int x, int y)
        global_mixer->set_wb(Mixer::OUTPUT_INPUT0 + channel_number, r, g, b);
        previews[channel_number]->display->updateGL();
 }
+
+void MainWindow::audio_state_changed()
+{
+       post_to_main_thread([this]{
+               InputMapping mapping = global_audio_mixer->get_input_mapping();
+               for (unsigned bus_index = 0; bus_index < mapping.buses.size(); ++bus_index) {
+                       const InputMapping::Bus &bus = mapping.buses[bus_index];
+                       string suffix;
+                       if (bus.device.type == InputSourceType::ALSA_INPUT) {
+                               ALSAPool::Device::State state = global_audio_mixer->get_alsa_card_state(bus.device.index);
+                               if (state == ALSAPool::Device::State::STARTING) {
+                                       suffix = " (busy)";
+                               } else if (state == ALSAPool::Device::State::DEAD) {
+                                       suffix = " (dead)";
+                               }
+                       }
+
+                       audio_miniviews[bus_index]->bus_desc_label->setFullText(
+                               QString::fromStdString(bus.name + suffix));
+                       audio_expanded_views[bus_index]->bus_desc_label->setFullText(
+                               QString::fromStdString(bus.name + suffix));
+               }
+       });
+}
index 09fc887598d27c937a7d56a4042f75e880a0fdb0..374bd49d6e36961417c73f2733f0f189c921d1e6 100644 (file)
@@ -72,6 +72,8 @@ private:
        void audio_level_callback(float level_lufs, float peak_db, std::vector<AudioMixer::BusLevel> bus_levels, float global_level_lufs, float range_low_lufs, float range_high_lufs, float final_makeup_gain_db, float correlation);
        std::chrono::steady_clock::time_point last_audio_level_callback;
 
+       void audio_state_changed();
+
        Ui::MainWindow *ui;
        QLabel *disk_free_label;
        QPushButton *transition_btn1, *transition_btn2, *transition_btn3;
index 00f161a1052bcc7077951459282024be2999c5ba..d7f86558e16a98d4ada1be0632f9489c3eb9294f 100644 (file)
--- a/mixer.cpp
+++ b/mixer.cpp
@@ -281,6 +281,7 @@ void Mixer::configure_card(unsigned card_index, CaptureInterface *capture, bool
        DeviceSpec device{InputSourceType::CAPTURE_CARD, card_index};
        audio_mixer.reset_resampler(device);
        audio_mixer.set_name(device, card->capture->get_description());
+       audio_mixer.trigger_state_changed_callback();
 }