]> git.sesse.net Git - nageru/blobdiff - nageru/delay_analyzer.cpp
Begin working on a delay analyzer.
[nageru] / nageru / delay_analyzer.cpp
diff --git a/nageru/delay_analyzer.cpp b/nageru/delay_analyzer.cpp
new file mode 100644 (file)
index 0000000..7e25738
--- /dev/null
@@ -0,0 +1,127 @@
+#include "delay_analyzer.h"
+
+#include "audio_mixer.h"
+#include "ui_delay_analyzer.h"
+
+#include <bmusb/bmusb.h>
+
+#include <memory>
+
+using namespace bmusb;
+using namespace std;
+using namespace std::chrono;
+using namespace std::placeholders;
+
+DelayAnalyzer::DelayAnalyzer()
+       : ui(new Ui::DelayAnalyzer),
+         devices(global_audio_mixer->get_devices(AudioMixer::HOLD_NO_DEVICES))
+{
+       ui->setupUi(this);
+       connect(ui->grab_btn, &QPushButton::clicked, this, &DelayAnalyzer::grab_clicked);
+
+       connect(ui->card_combo_1, static_cast<void(QComboBox::*)(int)>(&QComboBox::currentIndexChanged),
+               bind(&DelayAnalyzer::card_selected, this, ui->card_combo_1, _1));
+       connect(ui->card_combo_2, static_cast<void(QComboBox::*)(int)>(&QComboBox::currentIndexChanged),
+               bind(&DelayAnalyzer::card_selected, this, ui->card_combo_2, _1));
+       connect(ui->channel_combo_1, static_cast<void(QComboBox::*)(int)>(&QComboBox::currentIndexChanged),
+               bind(&DelayAnalyzer::channel_selected, this, ui->channel_combo_1));
+       connect(ui->channel_combo_2, static_cast<void(QComboBox::*)(int)>(&QComboBox::currentIndexChanged),
+               bind(&DelayAnalyzer::channel_selected, this, ui->channel_combo_2));
+
+       for (const auto &spec_and_info : devices) {
+               QString label(QString::fromStdString(spec_and_info.second.display_name));
+               ui->card_combo_1->addItem(label + "   ", qulonglong(DeviceSpec_to_key(spec_and_info.first)));
+               ui->card_combo_2->addItem(label + "   ", qulonglong(DeviceSpec_to_key(spec_and_info.first)));
+       }
+
+       ui->peak_display_1->set_audio_clip(&clip1);
+       ui->peak_display_2->set_audio_clip(&clip2);
+}
+
+DelayAnalyzer::~DelayAnalyzer()
+{
+}
+
+void DelayAnalyzer::grab_clicked()
+{
+       grabbing = true;
+       clip1.clear();
+       clip2.clear();
+       ui->peak_display_1->audio_clip_updated();
+       ui->peak_display_2->audio_clip_updated();
+}
+
+void DelayAnalyzer::card_selected(QComboBox *card_combo, int selected_index)
+{
+       assert(card_combo == ui->card_combo_1 || card_combo == ui->card_combo_2);
+       QComboBox *channel_combo = (card_combo == ui->card_combo_1 ? ui->channel_combo_1 : ui->channel_combo_2);
+       int current_channel = channel_combo->currentIndex();
+
+       channel_combo->clear();
+
+       uint64_t key = card_combo->itemData(selected_index).toULongLong();
+       auto device_it = devices.find(key_to_DeviceSpec(key));
+       assert(device_it != devices.end());
+       unsigned num_device_channels = device_it->second.num_channels;
+       for (unsigned source = 0; source < num_device_channels; ++source) {
+               char buf[256];
+               snprintf(buf, sizeof(buf), "Channel %u   ", source + 1);
+               channel_combo->addItem(QString(buf));
+       }
+
+       if (current_channel != -1 && current_channel < channel_combo->count()) {
+               channel_combo->setCurrentIndex(current_channel);
+       } else {
+               channel_combo->setCurrentIndex(0);
+       }
+
+       if (card_combo == ui->card_combo_1) {
+               clip1.clear();
+               ui->peak_display_1->audio_clip_updated();
+       } else {
+               clip2.clear();
+               ui->peak_display_2->audio_clip_updated();
+       }
+}
+
+void DelayAnalyzer::channel_selected(QComboBox *channel_combo)
+{
+       if (channel_combo == ui->channel_combo_1) {
+               clip1.clear();
+               ui->peak_display_1->audio_clip_updated();
+       } else {
+               clip2.clear();
+               ui->peak_display_2->audio_clip_updated();
+       }
+}
+
+DeviceSpec DelayAnalyzer::get_selected_device(QComboBox *card_combo)
+{
+       return key_to_DeviceSpec(card_combo->currentData().toULongLong());
+}
+
+void DelayAnalyzer::add_audio(DeviceSpec device_spec, const uint8_t *data, unsigned num_samples, AudioFormat audio_format, steady_clock::time_point frame_time)
+{
+       unique_ptr<float[]> tmp;
+
+       if (device_spec == get_selected_device(ui->card_combo_1)) {
+               tmp.reset(new float[num_samples]);
+
+               convert_audio_to_fp32(tmp.get(), /*out_channel=*/0, /*out_num_channels=*/1, data, ui->channel_combo_1->currentIndex(), audio_format, num_samples);
+               clip1.add_audio(tmp.get(), num_samples, audio_format.sample_rate, frame_time);
+               ui->peak_display_1->audio_clip_updated();
+       }
+       if (device_spec == get_selected_device(ui->card_combo_2)) {
+               if (tmp == nullptr) {
+                       tmp.reset(new float[num_samples]);
+               }
+
+               convert_audio_to_fp32(tmp.get(), /*out_channel=*/0, /*out_num_channels=*/1, data, ui->channel_combo_2->currentIndex(), audio_format, num_samples);
+               clip2.add_audio(tmp.get(), num_samples, audio_format.sample_rate, frame_time);
+               ui->peak_display_2->audio_clip_updated();
+       }
+
+       if (clip1.get_length_seconds() >= 1.0 && clip2.get_length_seconds() >= 1.0) {
+               grabbing = false;
+       }
+}