Class anira::PrePostProcessor#

class PrePostProcessor#

Collaboration diagram for anira::PrePostProcessor:

digraph {
    graph [bgcolor="#00000000"]
    node [shape=rectangle style=filled fillcolor="#FFFFFF" font=Helvetica padding=2]
    edge [color="#1414CE"]
    "6" [label="anira::MemoryBlock< std::atomic< float > >" tooltip="anira::MemoryBlock< std::atomic< float > >"]
    "2" [label="anira::InferenceConfig" tooltip="anira::InferenceConfig"]
    "4" [label="anira::ModelData" tooltip="anira::ModelData"]
    "1" [label="anira::PrePostProcessor" tooltip="anira::PrePostProcessor" fillcolor="#BFBFBF"]
    "3" [label="anira::ProcessingSpec" tooltip="anira::ProcessingSpec"]
    "5" [label="anira::TensorShape" tooltip="anira::TensorShape"]
    "2" -> "3" [dir=forward tooltip="usage"]
    "2" -> "4" [dir=forward tooltip="usage"]
    "2" -> "5" [dir=forward tooltip="usage"]
    "1" -> "2" [dir=forward tooltip="usage"]
    "1" -> "6" [dir=forward tooltip="usage"]
}

Abstract base class for preprocessing and postprocessing data for neural network inference.

The PrePostProcessor class handles the transformation of data between the host application and neural network inference engines. It provides default implementations for common use cases and serves as a base class for custom preprocessing implementations.

The class supports two types of tensor data:

  • Streamable tensors: Time-varying signals that flow continuously through ring buffers

  • Non-streamable tensors: Static parameters or control values stored in thread-safe internal storage

Key Features:

  • Thread-safe handling of non-streamable tensor data using atomic operations

  • Helper methods for efficient buffer manipulation

  • Support for multiple input/output tensors with different characteristics

  • Real-time safe operations suitable for processing

Usage:

For models that operate in the time domain with simple input/output shapes, the default implementation can be used directly. For custom preprocessing requirements (frequency domain transforms, custom windowing, multi-tensor operations), inherit from this class and override the pre_process() and post_process() methods.

Warning

All methods are designed to be real-time safe and should not perform memory allocation or other blocking operations when called from the audio thread.

Public Functions

PrePostProcessor() = delete#

Default constructor is deleted to prevent uninitialized instances.

PrePostProcessor(InferenceConfig &inference_config)#

Constructs a PrePostProcessor with the given inference configuration.

Initializes internal storage for non-streamable tensors based on the configuration. Streamable tensors (those with preprocess_input_size > 0 or postprocess_output_size > 0) do not require internal storage as they use ring buffers directly.

Parameters:

inference_config – Reference to the inference configuration containing tensor specifications

virtual ~PrePostProcessor() = default#

Default destructor.

virtual void pre_process(std::vector<RingBuffer> &input, std::vector<BufferF> &output, InferenceBackend current_inference_backend)#

Transforms input data from ring buffers to inference tensors.

This method is called before neural network inference to prepare input data. For streamable tensors, it extracts samples from ring buffers. For non-streamable tensors, it retrieves values from internal storage.

Note

This method is called from the audio thread and must be real-time safe

Parameters:
  • input – Vector of input ring buffers containing data from the host application

  • output – Vector of output tensors that will be fed to the inference engine

  • current_inference_backend – Currently active inference backend (for backend-specific processing)

virtual void post_process(std::vector<BufferF> &input, std::vector<RingBuffer> &output, InferenceBackend current_inference_backend)#

Transforms inference results to output ring buffers.

This method is called after neural network inference to process the results. For streamable tensors, it pushes samples to ring buffers. For non-streamable tensors, it stores values in internal storage.

Note

This method is called from the audio thread and must be real-time safe

Parameters:
  • input – Vector of input tensors containing inference results

  • output – Vector of output ring buffers that will be read by the host application

  • current_inference_backend – Currently active inference backend (for backend-specific processing)

inline virtual void before_inference(std::vector<BufferF> &input, InferenceBackend current_inference_backend)#

Hook called on the inference thread immediately before the backend runs.

Override this to patch input tensors with data that must reflect the result of the previous inference (e.g. recurrent state feedback). This is necessary because pre_process() is called at submission time: when more than one inference is queued, all their input tensors have already been filled before the first one runs, so any state written there is stale. With session_exclusive_processor = true inferences execute strictly in submission order and never concurrently, which makes this hook the only safe place to splice in cross-inference state.

Note

Called on the inference thread, not the audio thread. The default implementation does nothing.

Parameters:
  • input – Vector of input tensors about to be fed to the inference engine

  • current_inference_backend – Currently active inference backend

inline virtual void after_inference(std::vector<BufferF> &output, InferenceBackend current_inference_backend)#

Hook called on the inference thread immediately after the backend runs.

Override this to capture output tensors that must be fed into the next inference (e.g. recurrent state feedback). Runs before the inference is marked done and before the next session-exclusive inference can be dispatched, so the state captured here is guaranteed to be visible to the next before_inference() call.

Note

Called on the inference thread, not the audio thread. The default implementation does nothing.

Parameters:
  • output – Vector of output tensors produced by the inference engine

  • current_inference_backend – Currently active inference backend

void set_input(const float &input, size_t i, size_t j)#

Sets a non-streamable input value in thread-safe storage.

Used to store control parameters or static values that don’t change sample-by-sample. The data is stored using atomic operations for thread safety.

See also

get_input()

Warning

Only use for tensors where preprocess_input_size == 0

Parameters:
  • input – The value to store

  • i – Tensor index (which input tensor)

  • j – Sample index within the tensor

void set_output(const float &output, size_t i, size_t j)#

Sets a non-streamable output value in thread-safe storage.

Used to store control parameters or static values from inference results. The data is stored using atomic operations for thread safety.

See also

get_output()

Warning

Only use for tensors where postprocess_output_size == 0

Parameters:
  • output – The value to store

  • i – Tensor index (which output tensor)

  • j – Sample index within the tensor

float get_input(size_t i, size_t j)#

Retrieves a non-streamable input value from thread-safe storage.

Used to read control parameters or static values in a thread-safe manner.

See also

set_input()

Warning

Only use for tensors where preprocess_input_size == 0

Parameters:
  • i – Tensor index (which input tensor)

  • j – Sample index within the tensor

Returns:

The stored input value

float get_output(size_t i, size_t j)#

Retrieves a non-streamable output value from thread-safe storage.

Used to read inference results or control parameters in a thread-safe manner.

See also

set_output()

Warning

Only use for tensors where postprocess_output_size == 0

Parameters:
  • i – Tensor index (which output tensor)

  • j – Sample index within the tensor

Returns:

The stored output value

void pop_samples_from_buffer(RingBuffer &input, BufferF &output, size_t num_samples)#

Extracts samples from a ring buffer to an output tensor.

Pops the specified number of samples from the ring buffer and writes them to the output tensor. For multi-channel inputs, samples are interleaved in the output buffer (channel 0 samples first, then channel 1, etc.).

Note

Real-time safe operation

Parameters:
  • input – Source ring buffer

  • output – Destination tensor buffer

  • num_samples – Number of samples to extract per channel

void pop_samples_from_buffer(RingBuffer &input, BufferF &output, size_t num_new_samples, size_t num_old_samples)#

Extracts samples with overlapping windows from a ring buffer.

Combines new samples with previously extracted samples to create overlapping windows. This is useful for models that require context from previous inference steps.

Note

Real-time safe operation

Parameters:
  • input – Source ring buffer

  • output – Destination tensor buffer

  • num_new_samples – Number of new samples to extract per channel

  • num_old_samples – Number of samples to retain from previous extraction

void pop_samples_from_buffer(RingBuffer &input, BufferF &output, size_t num_new_samples, size_t num_old_samples, size_t offset)#

Extracts samples with overlapping windows and offset.

Advanced version that allows specifying an offset in the output buffer where the extracted samples should be written. Useful for batched processing or complex tensor layouts.

Note

Real-time safe operation

Parameters:
  • input – Source ring buffer

  • output – Destination tensor buffer

  • num_new_samples – Number of new samples to extract per channel

  • num_old_samples – Number of samples to retain from previous extraction

  • offset – Starting position in the output buffer for writing samples

void pop_samples_from_buffer(RingBuffer &input, BufferF &output, size_t num_new_samples, size_t num_old_samples, size_t offset, size_t num_batches)#

Extracts a batch of overlapping windows in a single call.

Repeats the offset overload num_batches times, advancing the output offset by the window size (num_new_samples + num_old_samples) for each batch. This produces a contiguously laid-out batched tensor — batch b occupies [offset + b * window_size, offset + (b + 1) * window_size) — as required by models whose input shape is [num_batches, ..., window_size] (e.g. the HybridNN/GuitarLSTM windowing).

Equivalent to calling the offset overload in a loop, but keeps the loop in native code. This matters for the WebAssembly build, where doing the loop in JavaScript incurs one JS↔Wasm boundary crossing per batch element and can dominate the audio-thread budget for large batches.

Note

Real-time safe operation

Parameters:
  • input – Source ring buffer

  • output – Destination tensor buffer

  • num_new_samples – Number of new samples to extract per channel and batch

  • num_old_samples – Number of past samples to retain per channel and batch

  • offset – Starting position in the output buffer for the first batch

  • num_batches – Number of consecutive windows to extract

void push_samples_to_buffer(const BufferF &input, RingBuffer &output, size_t num_samples)#

Writes samples from a tensor to a ring buffer.

Pushes samples from the input tensor to the ring buffer. For multi-channel outputs, assumes samples are interleaved in the input buffer (channel 0 samples first, then channel 1, etc.).

Note

Real-time safe operation

Parameters:
  • input – Source tensor buffer

  • output – Destination ring buffer

  • num_samples – Number of samples to write per channel