The core processing function that must be implemented by derived classes.
This method is where the actual transformation logic is implemented. It should contain the algorithmic details of how the buffer's data is transformed, analyzed, or processed. The implementation can utilize any backend capabilities available to the processor, including:
Derived classes must override this method to provide specific processing behavior.
315{
316 auto audio = std::dynamic_pointer_cast<AudioBuffer>(
buffer);
317 if (!audio) {
318 return;
319 }
320
321 const bool use_step =
static_cast<bool>(
m_step);
323 if (!use_step && !use_kernel && !
m_combine) {
324 return;
325 }
326
327 auto& data = audio->get_data();
328 const size_t samples = data.size();
329 const size_t taps =
m_taps.size();
330 if (samples == 0 || taps == 0) {
331 return;
332 }
333
335 auto& ring = *state.ring;
336
339 }
340
341 if (state.values.size() != taps) {
342 state.values.assign(taps, 0.0);
343 state.window.assign(taps + 1, 0.0);
344 state.lag_prev.assign(taps, 1.0);
345 state.lag_now.assign(taps, 1.0);
346 state.primed = false;
347 }
348
350 const bool foreign =
source &&
static_cast<const Buffer*
>(
source.get()) !=
buffer.get();
351
352 const Memory::HistoryBuffer<double>* read_ring = ˚
354 if (foreign) {
355 auto& source_ring =
source->get_history_buffer();
356 if (source_ring.capacity() <
m_capacity + samples) {
358 }
359 read_ring = &source_ring;
360 offset =
static_cast<double>(
source->get_write_count()) -
static_cast<double>(state.elapsed);
361 }
362
365 const auto seconds =
static_cast<double>(state.elapsed) /
rate;
366 const double lowest = foreign ? std::max(1.0, static_cast<double>(samples)) : 1.0;
367 const double highest = std::max(
static_cast<double>((foreign ?
m_capacity : ring.capacity()) - extra), lowest);
368
369 for (size_t t = 0; t < taps; ++t) {
371 state.lag_now[t] = std::clamp(raw, lowest, highest);
372 }
373 if (!state.primed) {
374 state.lag_prev = state.lag_now;
375 state.primed = true;
376 }
377
378 const double inverse = 1.0 / static_cast<double>(samples);
379 const bool plain_read = !foreign &&
m_order <= 1;
380
381 for (size_t i = 0; i < samples; ++i) {
382 const double ramp =
static_cast<double>(i + 1) * inverse;
383 const double shift = foreign ?
offset -
static_cast<double>(i) : 0.0;
384
385 for (size_t t = 0; t < taps; ++t) {
386 const double lag = state.lag_prev[t] + (state.lag_now[t] - state.lag_prev[t]) * ramp;
387 state.values[t] = plain_read ?
read(ring, lag) :
read_at(*read_ring, lag - 1.0 + shift,
m_order);
388 }
389
390 const std::span<const double> tap_values(state.values);
391 const std::span<double> coefs(state.coefs);
392
394 double carry {};
395 if (use_step) {
396 const auto [step_out, step_carry] =
m_step(data[i], tap_values, coefs);
398 carry = step_carry;
399 } else if (use_kernel) {
400 state.window[0] = data[i];
401 std::copy(state.values.begin(), state.values.end(), state.window.begin() + 1);
402 out =
m_kernel(std::span<double>(state.window), coefs);
404 } else {
407 }
408
410 ring.push(carry);
411 }
412
413 state.lag_prev = state.lag_now;
414 state.elapsed += samples;
415
416 if (
auto owner = std::dynamic_pointer_cast<FeedbackBuffer>(
buffer)) {
417 owner->mark_written(samples);
418 }
419}
std::shared_ptr< NetworkGeometryBuffer > buffer
std::vector< float > * out
std::vector< Tap > m_taps
std::weak_ptr< FeedbackBuffer > m_lag_source
static double read_at(const Memory::HistoryBuffer< double > &ring, double position, size_t order) noexcept
State & state_for(const std::shared_ptr< Buffer > &buffer)
static double read(const Memory::HistoryBuffer< double > &ring, double lag)
uint64_t s_registered_sample_rate
Global default sample rate.
double ramp(double edge0, double edge1, double x) noexcept
Hermite ramp from 0 at edge0 to 1 at edge1.