dspx 1.3.7 → 1.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,183 @@
1
+ /**
2
+ * TimeAlignmentStage.h
3
+ *
4
+ * Production-grade irregular timestamp resampling stage.
5
+ * Converts irregularly-sampled data to uniform time grid using true time-based interpolation.
6
+ *
7
+ * Key features:
8
+ * - Time-based coordinate system (not index-based)
9
+ * - Gap detection and configurable policies
10
+ * - Clock drift compensation
11
+ * - Multiple interpolation methods
12
+ * - SIMD-optimized search and interpolation
13
+ */
14
+
15
+ #pragma once
16
+
17
+ #include "../IDspStage.h"
18
+ #include <vector>
19
+ #include <string>
20
+
21
+ namespace dsp
22
+ {
23
+ namespace adapters
24
+ {
25
+ /**
26
+ * Gap detection and handling policy
27
+ */
28
+ enum class GapPolicy
29
+ {
30
+ ERROR, // Throw exception when gap detected
31
+ ZERO_FILL, // Fill gaps with zeros
32
+ HOLD, // Hold last valid value
33
+ INTERPOLATE, // Linear interpolation across gap
34
+ EXTRAPOLATE // Extrapolate beyond last sample
35
+ };
36
+
37
+ /**
38
+ * Interpolation method for regular samples
39
+ */
40
+ enum class InterpolationMethod
41
+ {
42
+ LINEAR, // Linear interpolation (fast, C0 continuous)
43
+ CUBIC, // Cubic spline (smooth, C1 continuous)
44
+ SINC // Windowed sinc (ideal lowpass, band-limited)
45
+ };
46
+
47
+ /**
48
+ * Clock drift compensation method
49
+ */
50
+ enum class DriftCompensation
51
+ {
52
+ NONE, // Use provided targetSampleRate as-is
53
+ REGRESSION, // Linear regression over input timestamps
54
+ PLL // Phase-locked loop (exponential moving average)
55
+ };
56
+
57
+ /**
58
+ * TimeAlignmentStage: Production-grade irregular timestamp resampling
59
+ *
60
+ * This stage solves the problems identified in Gemini's analysis:
61
+ * 1. ✅ Time-based coordinate system (not index-based)
62
+ * 2. ✅ Gap detection and handling policies
63
+ * 3. ✅ Clock drift compensation
64
+ * 4. ✅ Proper SIMD optimization for irregular data
65
+ * 5. ✅ Configurable extrapolation/error handling
66
+ *
67
+ * Usage:
68
+ * auto stage = TimeAlignmentStage(
69
+ * 100.0f, // targetSampleRate = 100 Hz
70
+ * InterpolationMethod::LINEAR, // interpolation method
71
+ * GapPolicy::INTERPOLATE, // gap policy
72
+ * 2.0f, // gapThreshold (2x expected interval)
73
+ * DriftCompensation::REGRESSION // drift compensation
74
+ * );
75
+ */
76
+ class TimeAlignmentStage : public IDspStage
77
+ {
78
+ public:
79
+ /**
80
+ * Constructor
81
+ * @param targetSampleRate Target uniform sample rate in Hz (e.g., 100.0 = 100 Hz)
82
+ * @param interpMethod Interpolation method for regular samples
83
+ * @param gapPolicy How to handle detected gaps (missing samples)
84
+ * @param gapThreshold Gap detection threshold (multiplier of expected interval)
85
+ * @param driftComp Clock drift compensation method
86
+ */
87
+ TimeAlignmentStage(
88
+ float targetSampleRate,
89
+ InterpolationMethod interpMethod = InterpolationMethod::LINEAR,
90
+ GapPolicy gapPolicy = GapPolicy::INTERPOLATE,
91
+ float gapThreshold = 2.0f,
92
+ DriftCompensation driftComp = DriftCompensation::NONE);
93
+
94
+ /**
95
+ * Process irregular data → uniform grid
96
+ * This is a resizing stage, so we override processResizing instead of process
97
+ */
98
+ void process(float *buffer, size_t numSamples, int numChannels, const float *timestamps = nullptr) override
99
+ {
100
+ // TimeAlignmentStage must use processResizing - this should not be called
101
+ throw std::runtime_error("TimeAlignmentStage::process() called - use processResizing()");
102
+ }
103
+
104
+ void processResizing(
105
+ const float *inputBuffer,
106
+ size_t inputSize,
107
+ float *outputBuffer,
108
+ size_t &outputSize,
109
+ int numChannels,
110
+ const float *timestamps = nullptr) override;
111
+
112
+ void reset() override;
113
+
114
+ // IDspStage serialization interface - Napi::Object version
115
+ Napi::Object serializeState(Napi::Env env) const override;
116
+ void deserializeState(const Napi::Object &state) override;
117
+
118
+ // TOON binary serialization
119
+ void serializeToon(toon::Serializer &serializer) const override;
120
+ void deserializeToon(toon::Deserializer &deserializer) override;
121
+
122
+ // IDspStage resizing interface
123
+ bool isResizing() const override;
124
+ size_t calculateOutputSize(size_t inputSize) const override;
125
+ double getTimeScaleFactor() const override;
126
+ const char *getType() const override { return "timeAlignment"; }
127
+
128
+ /**
129
+ * Get statistics from last processing
130
+ */
131
+ struct Statistics
132
+ {
133
+ size_t inputSamples; // Input sample count
134
+ size_t outputSamples; // Output sample count
135
+ size_t gapsDetected; // Number of gaps detected
136
+ float estimatedSampleRate; // Estimated input sample rate (Hz)
137
+ float timeSpanMs; // Total time span processed (ms)
138
+ float minGapDurationMs; // Smallest gap detected (ms)
139
+ float maxGapDurationMs; // Largest gap detected (ms)
140
+ float avgIntervalMs; // Average input interval (ms)
141
+ float stdDevIntervalMs; // Std dev of input intervals (ms)
142
+ };
143
+
144
+ Statistics getStatistics() const { return m_stats; }
145
+
146
+ private:
147
+ // Configuration
148
+ float m_targetSampleRate;
149
+ InterpolationMethod m_interpMethod;
150
+ GapPolicy m_gapPolicy;
151
+ float m_gapThreshold;
152
+ DriftCompensation m_driftComp;
153
+
154
+ // Statistics
155
+ Statistics m_stats;
156
+
157
+ // Drift compensation state
158
+ float m_estimatedSampleRate;
159
+ size_t m_driftWindowSize;
160
+
161
+ // Cached values from last processing
162
+ mutable double m_lastTimeScaleFactor;
163
+ mutable float m_lastStartTime;
164
+ mutable float m_lastEndTime;
165
+
166
+ // Helper methods
167
+ void estimateSampleRate(const float *timestamps, size_t numSamples, int channels);
168
+ void detectGaps(const float *timestamps, size_t numSamples, int channels, std::vector<size_t> &gapIndices);
169
+ float interpolate(float targetTime, const float *timestamps, const float *samples,
170
+ size_t numSamples, int channels, int channel, size_t &searchStart);
171
+ void interpolateLinear(float targetTime, const float *timestamps, const float *samples,
172
+ size_t numSamples, int channels, int channel, size_t &searchStart, float &output);
173
+ void interpolateCubic(float targetTime, const float *timestamps, const float *samples,
174
+ size_t numSamples, int channels, int channel, size_t &searchStart, float &output);
175
+ void interpolateSinc(float targetTime, const float *timestamps, const float *samples,
176
+ size_t numSamples, int channels, int channel, size_t &searchStart, float &output);
177
+
178
+ // SIMD-optimized search
179
+ size_t findBracketingInterval(float targetTime, const float *timestamps, size_t numSamples, int channels, size_t searchStart);
180
+ };
181
+
182
+ } // namespace adapters
183
+ } // namespace dsp