dspx 1.4.8 → 1.4.10
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/package.json
CHANGED
|
Binary file
|
|
@@ -342,19 +342,19 @@ namespace dsp
|
|
|
342
342
|
throw std::runtime_error("setState() requires stateful mode");
|
|
343
343
|
}
|
|
344
344
|
|
|
345
|
-
// Validate size - must match the power-of-2 buffer size, not coefficient count
|
|
346
|
-
if (state.size() != m_state.size())
|
|
347
|
-
{
|
|
348
|
-
throw std::invalid_argument("state size must match internal buffer size");
|
|
349
|
-
}
|
|
350
|
-
if (stateIndex >= state.size() && !state.empty())
|
|
351
|
-
{
|
|
352
|
-
throw std::invalid_argument("stateIndex out of range");
|
|
353
|
-
}
|
|
354
|
-
|
|
355
345
|
#if defined(__ARM_NEON) || defined(__aarch64__)
|
|
356
346
|
if (m_useNeon && m_neonFilter)
|
|
357
347
|
{
|
|
348
|
+
// Validate against NEON buffer size on ARM
|
|
349
|
+
if (state.size() != m_neonFilter->getBufferSize())
|
|
350
|
+
{
|
|
351
|
+
throw std::invalid_argument("state size must match internal buffer size");
|
|
352
|
+
}
|
|
353
|
+
if (stateIndex >= state.size() && !state.empty())
|
|
354
|
+
{
|
|
355
|
+
throw std::invalid_argument("stateIndex out of range");
|
|
356
|
+
}
|
|
357
|
+
|
|
358
358
|
// Convert to float vector for NEON filter's linearization
|
|
359
359
|
std::vector<float> floatState(state.size());
|
|
360
360
|
for (size_t i = 0; i < state.size(); ++i)
|
|
@@ -365,6 +365,17 @@ namespace dsp
|
|
|
365
365
|
return;
|
|
366
366
|
}
|
|
367
367
|
#endif
|
|
368
|
+
|
|
369
|
+
// Validate size - must match the power-of-2 buffer size, not coefficient count
|
|
370
|
+
if (state.size() != m_state.size())
|
|
371
|
+
{
|
|
372
|
+
throw std::invalid_argument("state size must match internal buffer size");
|
|
373
|
+
}
|
|
374
|
+
if (stateIndex >= state.size() && !state.empty())
|
|
375
|
+
{
|
|
376
|
+
throw std::invalid_argument("stateIndex out of range");
|
|
377
|
+
}
|
|
378
|
+
|
|
368
379
|
m_state = state;
|
|
369
380
|
m_stateIndex = stateIndex;
|
|
370
381
|
}
|
|
@@ -233,58 +233,52 @@ namespace dsp::core
|
|
|
233
233
|
*/
|
|
234
234
|
std::pair<std::vector<float>, size_t> exportLinearState() const
|
|
235
235
|
{
|
|
236
|
-
#if defined(__ARM_NEON) || defined(__aarch64__)
|
|
237
236
|
const float *state = getState();
|
|
238
237
|
std::vector<float> linearState(m_bufferSize, 0.0f);
|
|
239
238
|
|
|
240
|
-
//
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
239
|
+
// Extract the circular buffer into linear format (oldest->newest)
|
|
240
|
+
// m_head points to the position of the most recent sample
|
|
241
|
+
// The convolution reads from (m_head - numTaps + 1), which is the oldest sample position
|
|
242
|
+
|
|
243
|
+
// Calculate the read start position (oldest valid sample)
|
|
244
|
+
size_t readStart = (m_head + m_bufferSize - m_numTaps + 1) & m_headMask;
|
|
244
245
|
|
|
245
|
-
//
|
|
246
|
+
// Copy samples in order: oldest->newest into linear positions [0..bufferSize-1]
|
|
246
247
|
for (size_t i = 0; i < m_bufferSize; ++i)
|
|
247
248
|
{
|
|
248
|
-
linearState[i] = state[(
|
|
249
|
+
linearState[i] = state[(readStart + i) & m_headMask];
|
|
249
250
|
}
|
|
250
251
|
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
const float *state = getState();
|
|
255
|
-
std::vector<float> linearState(state, state + m_bufferSize);
|
|
256
|
-
return {linearState, m_head};
|
|
257
|
-
#endif
|
|
252
|
+
// Return 0 as stateIndex to indicate the next write should go to position 0
|
|
253
|
+
// (overwriting the oldest sample in the linear layout)
|
|
254
|
+
return {linearState, 0};
|
|
258
255
|
}
|
|
259
256
|
|
|
260
257
|
/**
|
|
261
258
|
* @brief Import state from linear format (oldest->newest) after deserialization
|
|
262
259
|
* @param linearState Linear state vector (oldest->newest)
|
|
263
|
-
* @param stateIndex State index (
|
|
260
|
+
* @param stateIndex State index (must be 0 for linear layout)
|
|
264
261
|
*/
|
|
265
262
|
void importLinearState(const std::vector<float> &linearState, size_t stateIndex)
|
|
266
263
|
{
|
|
267
|
-
#if defined(__ARM_NEON) || defined(__aarch64__)
|
|
268
264
|
float *state = getState();
|
|
269
265
|
|
|
270
|
-
// Copy linear state into circular buffer
|
|
266
|
+
// Copy the linear state directly into the circular buffer starting at position 0
|
|
267
|
+
// This creates a "linearized" layout where oldest is at 0, newest at numTaps-1
|
|
271
268
|
for (size_t i = 0; i < m_bufferSize && i < linearState.size(); ++i)
|
|
272
269
|
{
|
|
273
270
|
state[i] = linearState[i];
|
|
274
271
|
state[i + m_bufferSize] = linearState[i]; // Guard zone
|
|
275
272
|
}
|
|
276
273
|
|
|
277
|
-
// Set
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
//
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
}
|
|
286
|
-
m_head = stateIndex;
|
|
287
|
-
#endif
|
|
274
|
+
// Set m_head to point to the newest sample (last valid position)
|
|
275
|
+
// After import: oldest=0, newest=(numTaps-1)
|
|
276
|
+
// m_head should point to position (numTaps - 1)
|
|
277
|
+
// Next write will go to position numTaps, which is correct
|
|
278
|
+
m_head = (m_numTaps > 0) ? (m_numTaps - 1) : 0;
|
|
279
|
+
|
|
280
|
+
// Mark buffer as filled so we don't return zeros during transient phase
|
|
281
|
+
m_samplesProcessed = m_numTaps;
|
|
288
282
|
}
|
|
289
283
|
|
|
290
284
|
size_t getNumTaps() const { return m_numTaps; }
|