-
-
Notifications
You must be signed in to change notification settings - Fork 1.3k
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 0a6694b
Showing
7 changed files
with
203 additions
and
28 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,85 @@ | ||
#include "util/hosttimefilter.h" | ||
|
||
#include <gtest/gtest.h> | ||
|
||
#include <chrono> | ||
|
||
using namespace std::chrono_literals; | ||
|
||
namespace mixxx { | ||
|
||
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); | ||
} | ||
|
||
} // namespace mixxx |
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,84 @@ | ||
#pragma once | ||
|
||
#include <chrono> | ||
#include <utility> | ||
#include <vector> | ||
|
||
namespace mixxx { | ||
|
||
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)); | ||
} | ||
}; | ||
|
||
} // namespace mixxx |