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)
return samples_out;
}
+
+vector<string> AudioMixer::get_names() const
+{
+ vector<string> names;
+ for (unsigned card_index = 0; card_index < num_cards; ++card_index) {
+ const CaptureCard *card = &cards[card_index];
+ unique_lock<mutex> 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<mutex> lock(card->audio_mutex);
+ card->name = name;
+}
+
+void AudioMixer::set_input_mapping(const InputMapping &input_mapping)
+{
+ lock_guard<mutex> lock(mapping_mutex);
+ this->input_mapping = input_mapping;
+}
+
+InputMapping AudioMixer::get_input_mapping() const
+{
+ lock_guard<mutex> lock(mapping_mutex);
+ return input_mapping;
+}
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<Input> inputs;
+};
+
class AudioMixer {
public:
AudioMixer(unsigned num_cards);
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<std::string> 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)
{
unsigned num_cards;
struct CaptureCard {
- std::mutex audio_mutex;
- std::unique_ptr<ResamplingQueue> resampling_queue; // Under audio_mutex.
- int64_t next_local_pts = 0; // Beginning of next frame, in TIMEBASE units. Under audio_mutex.
std::atomic<float> fader_volume_db{0.0f};
+
+ // Everything below audio_mutex is protected by it.
+ mutable std::mutex audio_mutex;
+ std::unique_ptr<ResamplingQueue> resampling_queue;
+ int64_t next_local_pts = 0;
+ std::string name;
};
CaptureCard cards[MAX_CARDS];
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)
--- /dev/null
+#include "input_mapping_dialog.h"
+
+#include "ui_input_mapping.h"
+
+#include <QComboBox>
+
+using namespace std;
+
+InputMappingDialog::InputMappingDialog()
+ : ui(new Ui::InputMappingDialog)
+{
+ ui->setupUi(this);
+
+ //connect(ui->button_box, &QDialogButtonBox::accepted, [this]{ this->close(); });
+ vector<string> 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<string> &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);
+ }
+}
InputMappingDialog();
private:
+ void fill_ui_from_mapping(const InputMapping &mapping, const std::vector<std::string> &card_names);
+ void fill_channel_ui_from_mapping(unsigned row, const InputMapping::Input &input);
+
Ui::InputMappingDialog *ui;
};
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());
}
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>InputMappingDialog</class>
- <widget class="QDialog" name="Dialog">
+ <widget class="QDialog" name="InputMappingDialog">
<property name="geometry">
<rect>
<x>0</x>
</property>
<layout class="QVBoxLayout" name="verticalLayout_2">
<item>
- <widget class="QTableWidget" name="tableWidget">
+ <widget class="QTableWidget" name="table">
+ <column>
+ <property name="text">
+ <string/>
+ </property>
+ </column>
<column>
<property name="text">
<string>Device</string>
<string/>
</property>
<property name="icon">
- <iconset theme="list-add"/>
+ <iconset theme="list-add">
+ <normaloff>.</normaloff>.</iconset>
</property>
</widget>
</item>
<string/>
</property>
<property name="icon">
- <iconset theme="go-up"/>
+ <iconset theme="go-up">
+ <normaloff>.</normaloff>.</iconset>
</property>
</widget>
</item>
<string/>
</property>
<property name="icon">
- <iconset theme="go-down"/>
+ <iconset theme="go-down">
+ <normaloff>.</normaloff>.</iconset>
</property>
</widget>
</item>