From 4bbcd111d04f36a42cf3d40f18fcee5a91c6322a Mon Sep 17 00:00:00 2001 From: "Steinar H. Gunderson" Date: Sun, 31 Jul 2016 01:26:59 +0200 Subject: [PATCH] Store an input mapping, and show it in the UI. Edits you do in the UI are not actually editable yet, and the mixer doesn't care about the mapping either. --- audio_mixer.cpp | 40 ++++++++++++++++++++++++ audio_mixer.h | 30 ++++++++++++++++-- input_mapping_dialog.cpp | 67 ++++++++++++++++++++++++++++++++++++++++ input_mapping_dialog.h | 3 ++ mixer.cpp | 3 +- ui_input_mapping.ui | 18 ++++++++--- 6 files changed, 152 insertions(+), 9 deletions(-) create mode 100644 input_mapping_dialog.cpp diff --git a/audio_mixer.cpp b/audio_mixer.cpp index 2544ad1..15c65de 100644 --- a/audio_mixer.cpp +++ b/audio_mixer.cpp @@ -59,6 +59,16 @@ AudioMixer::AudioMixer(unsigned num_cards) set_compressor_enabled(global_flags.compressor_enabled); set_limiter_enabled(global_flags.limiter_enabled); set_final_makeup_gain_auto(global_flags.final_makeup_gain_auto); + + // Generate a very simple, default input mapping. + InputMapping::Input input; + input.name = "Main"; + input.input_source_type = InputSourceType::CAPTURE_CARD; + input.input_source_index = 0; + input.source_channel[0] = 0; + input.source_channel[1] = 1; + + input_mapping.inputs.push_back(input); } void AudioMixer::reset_card(unsigned card_index) @@ -259,3 +269,33 @@ vector AudioMixer::get_output(double pts, unsigned num_samples, Resamplin return samples_out; } + +vector AudioMixer::get_names() const +{ + vector names; + for (unsigned card_index = 0; card_index < num_cards; ++card_index) { + const CaptureCard *card = &cards[card_index]; + unique_lock lock(card->audio_mutex); + names.push_back(card->name); + } + return names; +} + +void AudioMixer::set_name(unsigned card_index, const string &name) +{ + CaptureCard *card = &cards[card_index]; + unique_lock lock(card->audio_mutex); + card->name = name; +} + +void AudioMixer::set_input_mapping(const InputMapping &input_mapping) +{ + lock_guard lock(mapping_mutex); + this->input_mapping = input_mapping; +} + +InputMapping AudioMixer::get_input_mapping() const +{ + lock_guard lock(mapping_mutex); + return input_mapping; +} diff --git a/audio_mixer.h b/audio_mixer.h index 8e15f54..6f7dbe5 100644 --- a/audio_mixer.h +++ b/audio_mixer.h @@ -29,6 +29,19 @@ namespace bmusb { struct AudioFormat; } // namespace bmusb +enum class InputSourceType { SILENCE, CAPTURE_CARD }; + +struct InputMapping { + struct Input { + std::string name; + InputSourceType input_source_type; + unsigned input_source_index; + int source_channel[2] { -1, -1 }; // Left and right. -1 = none. + }; + + std::vector inputs; +}; + class AudioMixer { public: AudioMixer(unsigned num_cards); @@ -43,6 +56,11 @@ public: void set_current_loudness(double level_lufs) { loudness_lufs = level_lufs; } void set_fader_volume(unsigned card_index, float level_db) { cards[card_index].fader_volume_db = level_db; } + std::vector get_names() const; + void set_name(unsigned card_index, const std::string &name); + + void set_input_mapping(const InputMapping &input_mapping); + InputMapping get_input_mapping() const; void set_locut_cutoff(float cutoff_hz) { @@ -153,10 +171,13 @@ private: unsigned num_cards; struct CaptureCard { - std::mutex audio_mutex; - std::unique_ptr resampling_queue; // Under audio_mutex. - int64_t next_local_pts = 0; // Beginning of next frame, in TIMEBASE units. Under audio_mutex. std::atomic fader_volume_db{0.0f}; + + // Everything below audio_mutex is protected by it. + mutable std::mutex audio_mutex; + std::unique_ptr resampling_queue; + int64_t next_local_pts = 0; + std::string name; }; CaptureCard cards[MAX_CARDS]; @@ -184,6 +205,9 @@ private: double final_makeup_gain = 1.0; // Under compressor_mutex. Read/write by the user. Note: Not in dB, we want the numeric precision so that we can change it slowly. bool final_makeup_gain_auto = true; // Under compressor_mutex. + + mutable std::mutex mapping_mutex; + InputMapping input_mapping; // Under mapping_mutex. }; #endif // !defined(_AUDIO_MIXER_H) diff --git a/input_mapping_dialog.cpp b/input_mapping_dialog.cpp new file mode 100644 index 0000000..ea20fdc --- /dev/null +++ b/input_mapping_dialog.cpp @@ -0,0 +1,67 @@ +#include "input_mapping_dialog.h" + +#include "ui_input_mapping.h" + +#include + +using namespace std; + +InputMappingDialog::InputMappingDialog() + : ui(new Ui::InputMappingDialog) +{ + ui->setupUi(this); + + //connect(ui->button_box, &QDialogButtonBox::accepted, [this]{ this->close(); }); + vector card_names = global_mixer->get_audio_mixer()->get_names(); + fill_ui_from_mapping(global_mixer->get_audio_mixer()->get_input_mapping(), card_names); +} + +void InputMappingDialog::fill_ui_from_mapping(const InputMapping &mapping, const vector &card_names) +{ + ui->table->verticalHeader()->hide(); + + ui->table->setRowCount(mapping.inputs.size()); + for (unsigned row = 0; row < mapping.inputs.size(); ++row) { + // TODO: Mark as some sort of header (by means of background color, probably). + QString name(QString::fromStdString(mapping.inputs[row].name)); + ui->table->setItem(row, 0, new QTableWidgetItem(name)); + + // Card choices. + QComboBox *card_combo = new QComboBox; + card_combo->addItem(QString("(none)")); + for (const string &name : card_names) { + card_combo->addItem(QString::fromStdString(name)); + } + switch (mapping.inputs[row].input_source_type) { + case InputSourceType::SILENCE: + card_combo->setCurrentIndex(0); + break; + case InputSourceType::CAPTURE_CARD: + card_combo->setCurrentIndex(mapping.inputs[row].input_source_index + 1); + break; + default: + assert(false); + } + ui->table->setCellWidget(row, 1, card_combo); + + // Left and right channel. + fill_channel_ui_from_mapping(row, mapping.inputs[row]); + } +} + +void InputMappingDialog::fill_channel_ui_from_mapping(unsigned row, const InputMapping::Input &input) +{ + for (unsigned channel = 0; channel < 2; ++channel) { + QComboBox *channel_combo = new QComboBox; + channel_combo->addItem(QString("(none)")); + if (input.input_source_type == InputSourceType::CAPTURE_CARD) { + for (unsigned source = 0; source < 8; ++source) { // TODO: Ask the card about number of channels, and names. + char buf[256]; + snprintf(buf, sizeof(buf), "Channel %u", source + 1); + channel_combo->addItem(QString(buf)); + } + } + channel_combo->setCurrentIndex(input.source_channel[channel] + 1); + ui->table->setCellWidget(row, 2 + channel, channel_combo); + } +} diff --git a/input_mapping_dialog.h b/input_mapping_dialog.h index c7fea24..4eb5e4c 100644 --- a/input_mapping_dialog.h +++ b/input_mapping_dialog.h @@ -20,6 +20,9 @@ public: InputMappingDialog(); private: + void fill_ui_from_mapping(const InputMapping &mapping, const std::vector &card_names); + void fill_channel_ui_from_mapping(unsigned row, const InputMapping::Input &input); + Ui::InputMappingDialog *ui; }; diff --git a/mixer.cpp b/mixer.cpp index b684b3d..e8bbc02 100644 --- a/mixer.cpp +++ b/mixer.cpp @@ -281,11 +281,12 @@ void Mixer::configure_card(unsigned card_index, CaptureInterface *capture, bool if (card->surface == nullptr) { card->surface = create_surface_with_same_format(mixer_surface); } - audio_mixer.reset_card(card_index); while (!card->new_frames.empty()) card->new_frames.pop(); card->fractional_samples = 0; card->last_timecode = -1; card->capture->configure_card(); + audio_mixer.reset_card(card_index); + audio_mixer.set_name(card_index, card->capture->get_description()); } diff --git a/ui_input_mapping.ui b/ui_input_mapping.ui index 82a0d3d..f2c28ab 100644 --- a/ui_input_mapping.ui +++ b/ui_input_mapping.ui @@ -1,7 +1,7 @@ InputMappingDialog - + 0 @@ -15,7 +15,12 @@ - + + + + + + Device @@ -53,7 +58,8 @@ - + + .. @@ -105,7 +111,8 @@ - + + .. @@ -121,7 +128,8 @@ - + + .. -- 2.39.2