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.
- package/README.md +26 -26
- package/binding.gyp +2 -1
- package/dist/bindings.d.ts +74 -0
- package/dist/bindings.d.ts.map +1 -1
- package/dist/bindings.js +94 -0
- package/dist/bindings.js.map +1 -1
- package/package.json +1 -1
- package/prebuilds/win32-x64/dspx.node +0 -0
- package/src/native/DspPipeline.cc +320 -81
- package/src/native/adapters/TimeAlignmentStage.cc +743 -0
- package/src/native/adapters/TimeAlignmentStage.h +183 -0
|
@@ -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
|