]> git.sesse.net Git - nageru/commitdiff
Make the delay analyzer understand that two sources can have different starting times.
authorSteinar H. Gunderson <steinar+nageru@gunderson.no>
Sun, 11 Aug 2019 08:50:55 +0000 (10:50 +0200)
committerSteinar H. Gunderson <steinar+nageru@gunderson.no>
Sun, 25 Aug 2019 22:39:08 +0000 (00:39 +0200)
If we didn't truncate the two sources so that they have different starting times,
the perceived delay would be skewed (unless the are different channels of the same
source, in which case they'd always be in sync anyway).

nageru/audio_clip.cpp
nageru/audio_clip.h
nageru/delay_analyzer.cpp
nageru/peak_display.cpp
nageru/peak_display.h

index 4dcb8db0135e4077d15e87fda3a942ad548c2b73..ec97e6b0f04543920ead2028b5f08cabeb788aae 100644 (file)
@@ -1,5 +1,6 @@
 #include "audio_clip.h"
 
+#include <assert.h>
 #include <math.h>
 
 using namespace std;
@@ -34,8 +35,31 @@ double AudioClip::get_length_seconds() const
        return double(vals.size()) / sample_rate;
 }
 
+double AudioClip::get_length_seconds_after_base(std::chrono::steady_clock::time_point base) const
+{
+       lock_guard<mutex> lock(mu);
+       if (vals.empty() || base < first_sample) {
+               // NOTE: base < first_sample can happen only during race conditions.
+               return 0.0;
+       }
+
+       return double(vals.size()) / sample_rate - duration<double>(base - first_sample).count();
+}
+
+bool AudioClip::empty() const
+{
+       lock_guard<mutex> lock(mu);
+       return vals.empty();
+}
+
+steady_clock::time_point AudioClip::get_first_sample() const
+{
+       lock_guard<mutex> lock(mu);
+       assert(!vals.empty());
+       return first_sample;
+}
 
-unique_ptr<pair<float, float>[]> AudioClip::get_min_max_peaks(unsigned width) const
+unique_ptr<pair<float, float>[]> AudioClip::get_min_max_peaks(unsigned width, steady_clock::time_point base) const
 {
        unique_ptr<pair<float, float>[]> min_max(new pair<float, float>[width]);
        for (unsigned x = 0; x < width; ++x) {
@@ -43,9 +67,10 @@ unique_ptr<pair<float, float>[]> AudioClip::get_min_max_peaks(unsigned width) co
        }
 
        lock_guard<mutex> lock(mu);
-       for (size_t i = 0; i < vals.size(); ++i) {
+       double skip_samples = duration<double>(base - first_sample).count() * sample_rate;
+       for (size_t i = int(floor(skip_samples)); i < vals.size(); ++i) {
                // We display one second.
-               int x = lrint(i * (double(width) / sample_rate));
+               int x = lrint((i - skip_samples) * (double(width) / sample_rate));
                if (x < 0 || x >= int(width)) continue;
                if (isnan(min_max[x].first)) {
                        min_max[x].first = min_max[x].second = 0.0;
index 71248af99cc5ba1cdef3f5ac081c8f6e00c8682d..693dbbd4aa863e141a4ac619ea24e59d10c23f56 100644 (file)
@@ -15,9 +15,14 @@ class AudioClip
 public:
        void clear();
        void add_audio(const float *samples, size_t num_samples, double sample_rate, std::chrono::steady_clock::time_point frame_time);
-       double get_length_seconds() const;
+       double get_length_seconds() const;  // 0.0 if empty().
+       double get_length_seconds_after_base(std::chrono::steady_clock::time_point base) const;
+       bool empty() const;
 
-       std::unique_ptr<std::pair<float, float>[]> get_min_max_peaks(unsigned width) const;
+       // Only valid if not empty.
+       std::chrono::steady_clock::time_point get_first_sample() const;
+
+       std::unique_ptr<std::pair<float, float>[]> get_min_max_peaks(unsigned width, std::chrono::steady_clock::time_point base) const;
 
 private:
        mutable std::mutex mu;
index 7e25738667e7330e0c29affb06337e2f51595858..e720c30a1472b4544c7f4a49a754d819129f25cb 100644 (file)
@@ -47,6 +47,8 @@ void DelayAnalyzer::grab_clicked()
        grabbing = true;
        clip1.clear();
        clip2.clear();
+       ui->peak_display_1->reset_base();
+       ui->peak_display_2->reset_base();
        ui->peak_display_1->audio_clip_updated();
        ui->peak_display_2->audio_clip_updated();
 }
@@ -121,7 +123,25 @@ void DelayAnalyzer::add_audio(DeviceSpec device_spec, const uint8_t *data, unsig
                ui->peak_display_2->audio_clip_updated();
        }
 
-       if (clip1.get_length_seconds() >= 1.0 && clip2.get_length_seconds() >= 1.0) {
+       if (clip1.empty() && clip2.empty()) {
+               // No interesting data yet.
+               return;
+       }
+
+       steady_clock::time_point base;
+       if (!clip1.empty() && !clip2.empty()) {
+               base = max(clip1.get_first_sample(), clip2.get_first_sample());
+       } else if (!clip1.empty()) {
+               base = clip1.get_first_sample();
+       } else {
+               assert(!clip2.empty());
+               base = clip2.get_first_sample();
+       }
+       ui->peak_display_1->set_base(base);
+       ui->peak_display_2->set_base(base);
+
+       if (clip1.get_length_seconds_after_base(base) >= 1.0 &&
+           clip2.get_length_seconds_after_base(base) >= 1.0) {
                grabbing = false;
        }
 }
index eda6b540fdbeffe4df6ee6799f03662512e99e29..c8e43913c42ef97cb2582ba978684506adedb85d 100644 (file)
@@ -11,6 +11,7 @@
 #include "audio_clip.h"
 
 using namespace std;
+using namespace std::chrono;
 
 PeakDisplay::PeakDisplay(QWidget *parent)
        : QWidget(parent)
@@ -24,8 +25,13 @@ void PeakDisplay::audio_clip_updated()
 
 void PeakDisplay::paintEvent(QPaintEvent *event)
 {
+       steady_clock::time_point paint_base = base;
+       if (paint_base == steady_clock::time_point() && !audio_clip->empty()) {
+               paint_base = audio_clip->get_first_sample();
+       }
+
        int w = width();
-       unique_ptr<pair<float, float>[]> min_max = audio_clip->get_min_max_peaks(w);
+       unique_ptr<pair<float, float>[]> min_max = audio_clip->get_min_max_peaks(w, paint_base);
 
        QPainter painter(this);
        painter.fillRect(event->rect(), Qt::white);
index a7a3b9365f40c4bb97123719c64e275cf275d0f3..76687a6ecb643aeba6185fd42c95fb97cb187683 100644 (file)
@@ -2,6 +2,7 @@
 #define PEAK_DISPLAY_H
 
 #include <QWidget>
+#include <chrono>
 #include <mutex>
 
 class AudioClip;
@@ -19,10 +20,23 @@ public:
        }
        void audio_clip_updated();
 
+       void set_base(std::chrono::steady_clock::time_point base)
+       {
+               this->base = base;
+               audio_clip_updated();
+       }
+
+       void reset_base()
+       {
+               base = std::chrono::steady_clock::time_point();
+               audio_clip_updated();
+       }
+
 private:
        void paintEvent(QPaintEvent *event) override;
 
        AudioClip *audio_clip;
+       std::chrono::steady_clock::time_point base;  // Epoch if not set.
 };
 
 #endif