Module Framework Architecture#
The Audio Processing Module Framework provides the standardized component interface and execution environment for all signal processing algorithms in Sound Open Firmware (SOF). By decoupling audio algorithms from low-level RTOS scheduling primitives, hardware platform drivers, and inter-processor communication (IPC) protocols, the module framework enables signal processing engineers to write portable, reusable audio processing blocks.
This architecture supports both statically linked in-tree processing modules (Volume, Equalizers, Mixers, Sample Rate Converters) and dynamically loaded third-party proprietary libraries (via Zephyr LLEXT), ensuring strict memory sandboxing and automated leak protection.
—
1. Architecture & Three-Tier Model#
The SOF module architecture is organized into three distinct tiers: the Standardized Module Interface, the Runtime Processing Module Instance, and the Module Adapter:
Figure 13 Three-Tier Architecture: Pipeline Schedulers to Concrete Audio Modules#
The Core Architectural Concepts#
Standardized Module Operations (`module_interface`): A uniform set of function callbacks (init, prepare, process, reset, free, and set_configuration) that every audio algorithm must implement. Because the interface is generic, the algorithm requires no knowledge of whether it is running on a real-time interrupt tick, inside an asynchronous RTOS worker thread, or within an offline simulation testbench.
Runtime Module Instance (`processing_module`): The runtime state of an instantiated module. It contains instance-specific metadata, negotiated audio format descriptors (sample rate, channel count, sample bit depth), memory pointers, and references to connected audio streams.
Module Adapter (`module_adapter`): The architectural glue and sandboxing layer. To the pipeline scheduler, the adapter looks like a standard pipeline component. Internally, it manages the module’s lifecycle, allocates dedicated memory, handles parameter blobs from host IPC messages, and dispatches audio samples through standardized input and output APIs.
—
2. The Module Adapter & Sandboxing Container#
The Module Adapter wraps internal DSP kernels and third-party processing engines, acting as a secure protective sandbox between the untrusted algorithm and the core operating system:
Figure 14 Module Adapter Container: Encapsulation, State Control, and IPC Translation#
Adapter Responsibilities#
Scheduler Translation: Translates pipeline commands (comp_new, comp_prepare, comp_copy, comp_free) into clean module callbacks (init, prepare, process, free).
Memory Isolation: Restricts module allocations to dedicated component memory heaps so that third-party code cannot corrupt global RTOS heaps.
Leak Protection: Automatically logs and frees any lingering module memory allocations when the component is destroyed.
Format Negotiation: Checks that incoming audio formats meet the module’s declared mathematical constraints (e.g., verifying that a 16-bit module does not receive unformatted 32-bit floating-point data).
—
3. Standardized Processing Interface: Source & Sink APIs#
In traditional audio drivers, processing components often access circular ring buffer memory directly through raw pointers. This tightly couples the algorithm to buffer wrap-around mathematics and DMA alignment quirks.
The SOF Module Framework decouples algorithms from buffers through the Source and Sink APIs. Modules operate in a clean “Get → Manipulate → Commit/Release” execution flow:
Figure 15 Source and Sink API Execution Pattern#
Source API (Inputs)#
Modules request readable frames by invoking
source_get_data().The API abstracts circular buffer wrap-around, providing safe contiguous memory blocks.
Upon completing execution, the module calls
source_release_data()with the exact number of frames consumed. If a module cannot process all available frames during this tick, unconsumed frames remain buffered for the next execution period.
Sink API (Outputs)#
Modules reserve writable space by invoking
sink_get_buffer().Once processed samples are written into the buffer, the module calls
sink_commit_buffer()with the number of valid produced frames.The commit operation makes the newly processed samples immediately visible to downstream components.
—
4. Pin Topologies & Stream Binding#
Audio modules connect to other components and buffers through directional pins:
Sink Pins (Inputs): Accept audio data streams from upstream components.
Source Pins (Outputs): Deliver processed audio streams to downstream components.
Figure 16 Supported Module Pin Topologies#
Dynamic Pin Binding#
Pins are not hard-coded into the firmware executable; they are dynamically bound and unbound at runtime based on topology directives or host IPC commands:
Binding (`comp_bind`): Connects an upstream module’s source pin to a downstream module’s sink pin through an intermediate audio buffer.
Unbinding (`comp_unbind`): Safely detaches pins when an audio pipeline is torn down or rerouted.
—
5. Module Runtime State Machine#
Every processing module is strictly governed by a uniform runtime state machine managed by the module_adapter. Modules must adhere to the transitions defined by enum module_state:
Figure 17 Module Runtime State Transition Diagram#
State Definitions#
`MODULE_DISABLED`: The module is uninstantiated or has been freed. Zero memory or execution slots are allocated.
`MODULE_INITIALIZED`: The module has successfully executed its .init() callback. It has parsed static initialization configuration parameters and allocated necessary internal structures (delay lines, coefficient arrays).
`MODULE_IDLE`: The module has executed .prepare(). Stream formats (sample rates, channel maps, sample bit depths) are fully negotiated and agreed upon. The algorithm is ready to stream.
`MODULE_PROCESSING`: The pipeline has issued a START trigger. The module’s .process() function is actively transforming audio buffers on every scheduling tick.
—
6. Parameter & Configuration Management#
Audio processing components require dynamic runtime tuning—such as adjusting equalizer cutoffs, modifying compressor thresholds, or setting speaker protection parameters.
The Module Framework separates configuration into three primary delivery channels:
Figure 18 Configuration Dispatch: Static Blobs, Runtime Blobs, and Scalar Controls#
Static Initialization Blobs: Delivered when the module is first instantiated via topology. Specifies initial configurations such as default filter modes or speaker models.
Large Runtime Blobs (Set Data): Used for multi-kilobyte binary payloads (e.g., acoustic echo cancellation calibration matrices, custom FIR filter impulse responses). Delivered over shared host-DSP SRAM mailboxes.
Immediate Scalar Values (Set Value): High-speed, lightweight commands used for volume faders, mute switches, or channel routing indices without allocation overhead.
—
7. Memory Sandboxing & Leak Protection#
To guarantee system stability, SOF isolates module allocations from global RTOS memory pools. This is especially vital when integrating third-party proprietary audio engines or dynamically loaded LLEXT modules:
Figure 19 Memory Sandboxing: Global System Heap vs Component Heap with Object Tracking#
Memory Protection Features#
Isolated Allocation Pool (`dp_heap_user`): Modules allocate scratch buffers and persistent delay lines from their assigned component heap partition rather than competing with kernel heaps.
Tracked Object Pool (`objpool`): Every allocation is registered in a tracking pool associated with the processing_module.
Automatic Garbage Collection on Teardown: When an audio stream closes, the Module Adapter calls
mod_free_all(). Even if a third-party algorithm neglects to free internal scratch buffers during its .free() callback, the adapter reclaims every registered memory block automatically, completely preventing memory leaks.
—