SolarOS

SolarOS 4.13.4 manual · api

Digital signal processing

service.dsp is a synchronous library service for signed 16-bit signal processing. Callers own FIR, decimator, and FFT contexts. The service does not run a worker task and does not retain global stream state.

The public operation selects its backend. Applications must not select SIMD directly. ESP32-S3 builds report esp32s3-pie and can use ESP-DSP PIE routines for eligible gain, window, and signed 16-bit FFT blocks. Other operations, small blocks, unaligned blocks, unavailable runtime work memory, and other targets use the portable implementation. service.engines records the operation under the dsp owner and reports cpu or simd use.

Numeric contract

FIR coefficient zero multiplies the newest sample. A decimator filters every input sample and emits the filtered value at phase zero. Its history and phase continue across calls until reset.

Native API

Include solar_os_dsp.h. Stateless operations are:

solar_os_dsp_dot_s16(a, b, count, &dot);
solar_os_dsp_gain_q15(output, input, count, gain_q15);
solar_os_dsp_mix_q15(output, a, b, count, gain_a_q15, gain_b_q15);
solar_os_dsp_clip_s16(output, input, count, minimum, maximum);
solar_os_dsp_level_s16(input, count, &level);
solar_os_dsp_window_q15(output, input, window_q15, count);

Streaming processors use caller-owned opaque contexts:

solar_os_dsp_fir_create(coefficients, taps, &fir);
solar_os_dsp_fir_process(fir, output, input, count);
solar_os_dsp_fir_reset(fir);
solar_os_dsp_fir_destroy(fir);

solar_os_dsp_decimator_create(coefficients, taps, factor, &decimator);
solar_os_dsp_decimator_process(decimator, output, capacity,
                               input, count, &produced);
solar_os_dsp_decimator_destroy(decimator);

solar_os_dsp_fft_create(size, &fft);
solar_os_dsp_fft_execute(fft, spectrum, input, &scale_exponent);
solar_os_dsp_fft_destroy(fft);

FIR filters support 1 through 1024 taps. FFT sizes must be powers of two from 2 through 4096. One context must not be used concurrently by multiple callers.

Use solar_os_dsp_backend(), solar_os_dsp_capabilities(), and solar_os_dsp_accelerated_capabilities() for diagnostics. Capability bits are:

BitOperation
0signed 16-bit dot product
1Q15 gain
2Q15 mix
3signed 16-bit clip
4signed 16-bit level
5Q15 window
6Q15 FIR
7Q15 decimator
8signed 16-bit FFT, PIE accelerated on eligible ESP32-S3 blocks

Python

Python accepts contiguous buffer objects containing native little-endian signed 16-bit samples. array('h') and bytearray objects are suitable. Stateless output functions return a new bytearray:

from array import array
import solaros

dsp = solaros.dsp
samples = array('h', [1000, -1000, 500, -500])

scaled = dsp.gain(samples, 0.5)
mixed = dsp.mix(samples, samples, 0.7, 0.3)
clipped = dsp.clip(samples, -800, 800)
peak, rms = dsp.level(samples)
energy = dsp.dot(samples, samples)

window(samples, coefficients) returns a new sample buffer. Both inputs must contain the same number of samples. Gains use floating-point values from -1.0 through 1.0 and are converted to Q15.

Stateful processors own their native context and release it during close() or garbage collection:

coefficients = array('h', [16384, 16384])
filt = dsp.fir(coefficients)
filtered = filt.process(samples)

downsample = dsp.fir(coefficients, decimation=4)
low_rate = downsample.process(samples)
downsample.reset()

fft = dsp.fft(1024)
spectrum, exponent = fft.execute(array('h', [0] * 1024))
fft.close()

The script bridge accepts at most 32768 samples per call.

Lua

Lua uses binary strings containing native little-endian signed 16-bit values. Stateless functions return a new binary string. FIR and FFT constructors return userdata that own the native context:

local dsp = solaros.dsp
local scaled = dsp.gain(samples, 0.5)
local peak, rms = dsp.level(samples)

local filt = dsp.fir(coefficients, 4)
local low_rate = filt:process(samples)
filt:reset()
filt:close()

local fft = dsp.fft(1024)
local spectrum, exponent = fft:execute(samples_1024)

Lua strings are immutable, so each output operation allocates a new string. The script bridge accepts at most 32768 samples per call.

Quick reference

Use service.dsp for synchronous fixed-point block processing. The service selects portable or ESP32-S3 PIE code without application-side board checks. Callers own streaming contexts and must destroy or close them. Python uses signed 16-bit buffer objects; Lua uses binary strings. The native Synth service uses level for its captured PCM scope blocks and publishes the resulting peak and RMS values through Synth status.

service.signal-widgets builds reusable oscilloscope and spectrum views on top of solar_os_gfx and this DSP API. Its submit calls copy recent mono or interleaved signed-16-bit PCM into thread-safe snapshots. Rendering the spectrum applies a Hann window and a 512-point FFT; the DSP service selects PIE SIMD on ESP32-S3 without widget-side board checks.

Join us on: