forked from mixxxdj/mixxx
-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Replaced Abletons HostTimeFilter by an own single header implementati…
…on, to keep the dependency to Ableton Link out of the sounddevices code
- Loading branch information
1 parent
29f7a1f
commit 721c8e9
Showing
7 changed files
with
207 additions
and
38 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,81 @@ | ||
#include "util/hosttimefilter.h" | ||
|
||
#include <gtest/gtest.h> | ||
|
||
#include <chrono> | ||
|
||
using namespace std::chrono_literals; | ||
|
||
class HostTimeFilterTest : public ::testing::Test { | ||
protected: | ||
HostTimeFilterTest() | ||
: m_filter(5) { // Initialize with 5 points for testing | ||
} | ||
|
||
HostTimeFilter m_filter; | ||
}; | ||
|
||
TEST_F(HostTimeFilterTest, InitialState) { | ||
EXPECT_EQ(m_filter.calcFilteredHostTime(0.0, 0us), 0us); | ||
} | ||
|
||
TEST_F(HostTimeFilterTest, AddSinglePoint) { | ||
EXPECT_NEAR(m_filter.calcFilteredHostTime(1.0, 1050us).count(), 1050, 1); | ||
} | ||
|
||
TEST_F(HostTimeFilterTest, EqualFreqNoJitter) { | ||
// Wo perfectly synced clocks, the filter should return the same host time as the auxiliary time | ||
EXPECT_NEAR(m_filter.calcFilteredHostTime(1000.0, 1000us).count(), 1000, 1); | ||
EXPECT_NEAR(m_filter.calcFilteredHostTime(2000.0, 2000us).count(), 2000, 1); | ||
EXPECT_NEAR(m_filter.calcFilteredHostTime(3000.0, 3000us).count(), 3000, 1); | ||
EXPECT_NEAR(m_filter.calcFilteredHostTime(4000.0, 4000us).count(), 4000, 1); | ||
EXPECT_NEAR(m_filter.calcFilteredHostTime(5000.0, 5000us).count(), 5000, 1); | ||
EXPECT_NEAR(m_filter.calcFilteredHostTime(6000.0, 6000us).count(), 6000, 1); | ||
EXPECT_NEAR(m_filter.calcFilteredHostTime(7000.0, 7000us).count(), 7000, 1); | ||
} | ||
|
||
TEST_F(HostTimeFilterTest, FasterFreqNoJitter) { | ||
// Use 1024 sample buffer interval, instead of auxiliarry clock in time units | ||
EXPECT_NEAR(m_filter.calcFilteredHostTime(1024.0, 1000us).count(), 1000, 1); | ||
EXPECT_NEAR(m_filter.calcFilteredHostTime(2048.0, 2000us).count(), 2000, 1); | ||
EXPECT_NEAR(m_filter.calcFilteredHostTime(3072.0, 3000us).count(), 3000, 1); | ||
EXPECT_NEAR(m_filter.calcFilteredHostTime(4096.0, 4000us).count(), 4000, 1); | ||
EXPECT_NEAR(m_filter.calcFilteredHostTime(5120.0, 5000us).count(), 5000, 1); | ||
EXPECT_NEAR(m_filter.calcFilteredHostTime(6144.0, 6000us).count(), 6000, 1); | ||
EXPECT_NEAR(m_filter.calcFilteredHostTime(7168.0, 7000us).count(), 7000, 1); | ||
} | ||
|
||
TEST_F(HostTimeFilterTest, FasterFreqWithJitter) { | ||
// Use 1024 sample buffer interval, with 100us host time jitter | ||
EXPECT_NEAR(m_filter.calcFilteredHostTime(1024.0, 1000us).count(), 1000, 1); | ||
EXPECT_NEAR(m_filter.calcFilteredHostTime(2048.0, 2100us).count(), 2100, 1); | ||
EXPECT_NEAR(m_filter.calcFilteredHostTime(3072.0, 3000us).count(), 3033, 1); | ||
EXPECT_NEAR(m_filter.calcFilteredHostTime(4096.0, 3900us).count(), 3940, 1); | ||
EXPECT_NEAR(m_filter.calcFilteredHostTime(5120.0, 5000us).count(), 4960, 1); | ||
EXPECT_NEAR(m_filter.calcFilteredHostTime(6144.0, 6000us).count(), 5960, 1); | ||
EXPECT_NEAR(m_filter.calcFilteredHostTime(7168.0, 7000us).count(), 7000, 1); | ||
} | ||
|
||
TEST_F(HostTimeFilterTest, FasterFreqSkippedPoints) { | ||
// Use 1024 sample buffer interval, instead of auxiliarry clock in time units | ||
EXPECT_NEAR(m_filter.calcFilteredHostTime(1024.0, 1000us).count(), 1000, 1); | ||
EXPECT_NEAR(m_filter.calcFilteredHostTime(2048.0, 2000us).count(), 2000, 1); | ||
EXPECT_NEAR(m_filter.calcFilteredHostTime(4096.0, 4000us).count(), 4000, 1); | ||
EXPECT_NEAR(m_filter.calcFilteredHostTime(5120.0, 5000us).count(), 5000, 1); | ||
EXPECT_NEAR(m_filter.calcFilteredHostTime(8192.0, 8000us).count(), 8000, 1); | ||
EXPECT_NEAR(m_filter.calcFilteredHostTime(9216.0, 9000us).count(), 9000, 1); | ||
EXPECT_NEAR(m_filter.calcFilteredHostTime(11264.0, 11000us).count(), 11000, 1); | ||
} | ||
|
||
TEST_F(HostTimeFilterTest, Reset) { | ||
m_filter.calcFilteredHostTime(1.0, 1050us); | ||
m_filter.calcFilteredHostTime(2.0, 1950us); | ||
m_filter.reset(); | ||
EXPECT_NEAR(m_filter.calcFilteredHostTime(4.0, 7777us).count(), 7777, 1); | ||
} | ||
|
||
TEST_F(HostTimeFilterTest, DenominatorZero) { | ||
// Add two identical points to ensure the denominator becomes zero | ||
EXPECT_NEAR(m_filter.calcFilteredHostTime(1.0, 1000us).count(), 1000, 1); | ||
EXPECT_NEAR(m_filter.calcFilteredHostTime(1.0, 1000us).count(), 1000, 1); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,80 @@ | ||
#pragma once | ||
|
||
#include <chrono> | ||
#include <utility> | ||
#include <vector> | ||
|
||
class HostTimeFilter { | ||
public: | ||
explicit HostTimeFilter(const std::size_t numPoints) | ||
: m_numPoints(numPoints), | ||
m_index(0), | ||
m_sumAux(0.0), | ||
m_sumHst(0.0), | ||
m_sumAuxByHst(0.0), | ||
m_sumAuxSquared(0.0) { | ||
m_points.reserve(m_numPoints); | ||
} | ||
|
||
void reset() { | ||
m_index = 0; | ||
m_points.clear(); | ||
m_sumAux = 0.0; | ||
m_sumHst = 0.0; | ||
m_sumAuxByHst = 0.0; | ||
m_sumAuxSquared = 0.0; | ||
} | ||
|
||
std::chrono::microseconds calcFilteredHostTime( | ||
double auxiliaryTime, std::chrono::microseconds hostTime) { | ||
const auto micros = hostTime.count(); | ||
const auto timePoint = std::make_pair(auxiliaryTime, static_cast<double>(micros)); | ||
|
||
if (m_points.size() < m_numPoints) { | ||
m_points.push_back(timePoint); | ||
m_sumAux += timePoint.first; | ||
m_sumHst += timePoint.second; | ||
m_sumAuxByHst += timePoint.first * timePoint.second; | ||
m_sumAuxSquared += timePoint.first * timePoint.first; | ||
} else { | ||
const auto& prevPoint = m_points[m_index]; | ||
m_sumAux += timePoint.first - prevPoint.first; | ||
m_sumHst += timePoint.second - prevPoint.second; | ||
m_sumAuxByHst += timePoint.first * timePoint.second - | ||
prevPoint.first * prevPoint.second; | ||
m_sumAuxSquared += timePoint.first * timePoint.first - | ||
prevPoint.first * prevPoint.first; | ||
m_points[m_index] = timePoint; | ||
} | ||
m_index = (m_index + 1) % m_numPoints; | ||
|
||
return linearRegression(timePoint); | ||
} | ||
|
||
private: | ||
const std::size_t m_numPoints; | ||
std::size_t m_index; | ||
std::vector<std::pair<double, double>> m_points; | ||
double m_sumAux; | ||
double m_sumHst; | ||
double m_sumAuxByHst; | ||
double m_sumAuxSquared; | ||
|
||
std::chrono::microseconds linearRegression(const std::pair<double, double>& timePoint) const { | ||
if (m_points.size() < 2) { | ||
return std::chrono::microseconds(static_cast<long long>(timePoint.second)); | ||
} | ||
|
||
const double n = static_cast<double>(m_points.size()); | ||
const double denominator = (n * m_sumAuxSquared - m_sumAux * m_sumAux); | ||
if (denominator == 0.0) { | ||
return std::chrono::microseconds(static_cast<long long>(timePoint.second)); | ||
} | ||
|
||
const double slope = (n * m_sumAuxByHst - m_sumAux * m_sumHst) / denominator; | ||
const double intercept = (m_sumHst - slope * m_sumAux) / n; | ||
|
||
return std::chrono::microseconds( | ||
static_cast<long long>(slope * timePoint.first + intercept)); | ||
} | ||
}; |