DSPi: A fully featured audio DSP firmware for the Raspberry Pi Pico (RP2040) and Pico 2 (RP2350)

Lobsters Hottest Tools

Summary

DSPi is an open-source firmware that turns a Raspberry Pi Pico into a fully featured digital audio processor, offering room correction, parametric EQ, matrix mixing, and more.

<p><a href="https://lobste.rs/s/bmlpaq/dspi_fully_featured_audio_dsp_firmware">Comments</a></p>
Original Article
View Cached Full Text

Cached at: 06/26/26, 08:06 AM

WeebLabs/DSPi

Source: https://github.com/WeebLabs/DSPi

DSPi Firmware

DSPi transforms a Raspberry Pi Pico or other RP2040-based board into a very competent and inexpensive little digital audio processor. It acts as a USB sound card with an onboard DSP engine, allowing you to make use of essential tools like room correction, active crossovers, parametric EQ, time alignment, loudness compensation, and headphone crossfeed.

It is my hope that the RP2040 and RP2350 will garner a reputation as the “swiss army knife of audio for less than a cup of coffee”.

Feel free to join the official Discord server for development updates, discussion or to request assistance!


Table of Contents


Key Capabilities

  • USB Audio Interface: Plug-and-play under macOS, Windows, Linux, and iOS. Supports 16-bit and 24-bit PCM input at 44.1, 48, and 96 kHz.
  • 24-bit S/PDIF or I2S Outputs: Up to four independent stereo output slots (8 channels on RP2350, 4 channels on RP2040). Each slot can be switched at runtime between S/PDIF and I2S, enabling direct connection to any standard DAC. I2S slots share a common BCK/LRCLK and can optionally produce a 128×/256× master clock.
  • Per-Channel Preamp: Independent gain control for each USB input channel (L/R), applied as PASS 1 of the DSP pipeline before any other processing.
  • Matrix Mixer: Route either or both USB input channels to any output with independent gain and phase invert per crosspoint. 2x9 on RP2350, 2x5 on RP2040.
  • Parametric Equalization: Up to 10 PEQ bands per channel with 6 filter types. 110 total filter bands on RP2350, 70 on RP2040. RP2350 uses a hybrid SVF/biquad architecture for superior low-frequency accuracy.
  • Volume Leveller: RMS-based, stereo-linked, soft-knee upward compressor that lifts quieter content toward a target level without ever amplifying loud passages. Optional 10 ms lookahead, configurable speed and max-gain ceiling, with a -6 dBFS gain-reduction safety limiter.
  • Loudness Compensation: Volume-dependent EQ based on the ISO 226:2003 equal-loudness contour standard. Automatically boosts bass and treble at low listening levels to maintain perceived tonal balance.
  • Headphone Crossfeed: BS2B-based crossfeed with interaural time delay (ITD) reduces unnatural stereo separation for headphone listening. Three classic presets plus fully custom parameters.
  • Master Volume: Device-side output ceiling (-128 to 0 dB, with a true-mute sentinel) applied at the very end of the signal chain, independent of USB host volume and DSP processing. Two persistence modes: stored independently of presets (default — survives reboots, unaffected by preset switching) or saved/restored as part of each preset.
  • Per-Output Gain & Mute: Independent gain and mute controls for each output channel.
  • Time Alignment: Per-output delay (up to 85ms) for speaker/subwoofer alignment with automatic latency compensation between S/PDIF/I2S and PDM output paths.
  • Subwoofer Output: Dedicated mono PDM output channel with a high-performance 2nd-order delta-sigma modulator, enabling direct subwoofer output without the need for a second DAC.
  • Dual-Core DSP: EQ processing is split across both cores on both platforms for maximum throughput when multiple outputs are active.
  • Configurable Output Pins: All output GPIO pins (including I2S BCK/MCK) can be reassigned at runtime to suit custom PCB layouts, no reflashing required.
  • 10-Slot Preset System: Save, load, and manage up to 10 complete DSP configurations with user-defined names. Includes per-channel naming, configurable startup slot, and bulk parameter transfer for fast state synchronization.
  • Diagnostics: Per-channel peak/clip metering, USB PHY error counters (CRC, bit-stuff, timeout, overflow, sequence), buffer fill statistics, S/PDIF DMA starvation counters per output slot, and CPU load reporting per core.
  • Firmware Update via USB: A vendor command reboots the device into the UF2 bootloader, allowing the host app to push new firmware without a physical BOOTSEL press.

Platform Support

FeatureRP2040 (Pico)RP2350 (Pico 2)
System Clock307.2 MHz (overclock)307.2 MHz
Core Voltage1.15 V1.15 V
Sample Rates44.1 / 48 / 96 kHz44.1 / 48 / 96 kHz
Audio ProcessingQ28 Fixed-PointSingle-Precision Float
EQ Bands10 per channel (70 total)10 per channel (110 total)
Total Channels7 (2 master + 4 S/PDIF·I2S + 1 PDM)11 (2 master + 8 S/PDIF·I2S + 1 PDM)
Output Slots2 stereo (each S/PDIF or I2S)4 stereo (each S/PDIF or I2S)
Output Bit Depth24-bit24-bit
PDM Output1 (subwoofer)1 (subwoofer)
Max Delay85ms per output85ms per output
Math EngineHand-optimized ARM AssemblyHardware FPU (hybrid SVF/biquad EQ)
Dual-Core EQYes (Core 1 processes outputs 3-4)Yes (Core 1 processes outputs 3-8)
User Presets10 slots10 slots
StatusProductionProduction

Both platforms are fully tested and production-ready. The RP2040 reaches 307.2 MHz with a slight voltage bump; the RP2350 hits the same frequency at the same voltage. Clock is fixed (no rate-dependent switching), and PIO dividers are integer at every supported sample rate. The RP2350 offers significantly more processing headroom thanks to its hardware floating-point unit, enabling more output channels and a hybrid SVF/biquad filter architecture for improved low-frequency accuracy.


Audio Signal Chain

DSPi processes audio in a linear, low-latency pipeline:

RP2350 (11 channels, 9 outputs):

USB Input (16/24-bit PCM Stereo, 44.1 / 48 / 96 kHz)
    |
PASS 1: Per-Channel Preamp (independent L/R gain) + USB Volume
    |
PASS 2: Master EQ (10 bands per channel, Left/Right)
    |
PASS 2.5: Volume Leveller (RMS upward compression, optional)
    |
PASS 3: Headphone Crossfeed (BS2B + ITD, optional) + Master Peak Metering
    |
        Loudness Compensation (volume-dependent EQ, optional)
    |
PASS 4: Matrix Mixer (2 inputs x 9 outputs, per-crosspoint gain & phase)
    |
PASS 5: Per-Output EQ -> Gain/Mute -> Delay -> Output Gain × Master Volume
    |
    +-- Out 1-2 --> S/PDIF or I2S slot 0 (data: GPIO 6 default)
    +-- Out 3-4 --> S/PDIF or I2S slot 1 (data: GPIO 7 default)
    +-- Out 5-6 --> S/PDIF or I2S slot 2 (data: GPIO 8 default)
    +-- Out 7-8 --> S/PDIF or I2S slot 3 (data: GPIO 9 default)
    +-- Out 9   --> PDM Sub               (data: GPIO 10 default)
                  (I2S BCK/LRCLK shared on GPIO 14/15 default; optional MCK on GPIO 13 default)

RP2040 (7 channels, 5 outputs):

USB Input (16/24-bit PCM Stereo, 44.1 / 48 / 96 kHz)
    |
PASS 1: Per-Channel Preamp + USB Volume
    |
PASS 2: Master EQ (10 bands per channel, Left/Right)
    |
PASS 2.5: Volume Leveller (RMS upward compression, optional)
    |
PASS 3: Headphone Crossfeed (BS2B + ITD, optional) + Master Peak Metering
    |
        Loudness Compensation (volume-dependent EQ, optional)
    |
PASS 4: Matrix Mixer (2 inputs x 5 outputs, per-crosspoint gain & phase)
    |
PASS 5: Per-Output EQ -> Gain/Mute -> Delay -> Output Gain × Master Volume
    |
    +-- Out 1-2 --> S/PDIF or I2S slot 0 (data: GPIO 6 default)
    +-- Out 3-4 --> S/PDIF or I2S slot 1 (data: GPIO 7 default)
    +-- Out 5   --> PDM Sub               (data: GPIO 10 default)
                  (I2S BCK/LRCLK shared on GPIO 14/15 default; optional MCK on GPIO 13 default)

Signal Chain Details

  1. Input (USB): 16-bit or 24-bit PCM stereo audio at 44.1, 48, or 96 kHz. Bit depth is selected via USB alt setting; sample rate via the USB Audio Class rate-set request.
  2. Per-Channel Preamp (PASS 1): Independent gain control for the USB Left and Right input channels in dB. Applied at the very start of the DSP chain so its setting affects all downstream processing.
  3. Master EQ (PASS 2): Up to 10 bands of parametric EQ per channel (Left/Right). Supports peaking, low shelf, high shelf, low pass, and high pass filter types.
  4. Volume Leveller (PASS 2.5): Optional feedforward, stereo-linked, single-band RMS compressor with soft-knee upward compression — quieter content is boosted toward a target level while content above the threshold passes through untouched. Configurable speed, max-gain ceiling, and noise gate. Optional 10 ms lookahead. A -6 dBFS gain-reduction safety limiter prevents output overshoots.
  5. Headphone Crossfeed (PASS 3): Optional BS2B crossfeed that mixes a filtered, delayed portion of each channel into the opposite channel. Uses a complementary filter design with interaural time delay (ITD) via an all-pass filter. Three presets (Default, Chu Moy, Jan Meier) plus custom frequency and feed level. ITD can be independently toggled. Master peak metering taps into this stage.
  6. Loudness Compensation: Optional ISO 226:2003 equal-loudness EQ that adapts to the current volume level. At low volumes, bass and treble are boosted to compensate for the ear’s reduced sensitivity. Configurable reference SPL and intensity. Driven by the USB host volume position so it remains correct regardless of master-volume attenuation downstream.
  7. Matrix Mixer (PASS 4): Routes the two USB input channels (Left/Right) to all output channels. Each crosspoint has independent enable, gain (-inf to +12 dB), and phase invert. Outputs can be individually enabled/disabled to save CPU. RP2350 has a 2x9 matrix (9 outputs), RP2040 has a 2x5 matrix (5 outputs).
  8. Output EQ (PASS 5): Independent 10-band EQ per output channel on both platforms. Ideal for crossover filters and per-driver correction. On RP2350, filters below Fs/7.5 use SVF topology for superior low-frequency accuracy; higher frequencies use traditional biquad.
  9. Per-Output Gain & Mute: Independent gain (-inf to +12 dB) and mute for each output channel.
  10. Time Alignment: Per-output delay for speaker alignment, up to 85 ms (4096 samples at 48 kHz). Automatic latency compensation between S/PDIF/I2S and PDM output paths.
  11. Master Volume: Device-side output ceiling, -128 to 0 dB with a true-mute sentinel at -128. Folded into the per-output multiplier at PASS 5 so it’s effectively free CPU-wise. Independent of the USB host volume — the two multiply together. Does not affect loudness-compensation behavior.
  12. Outputs: Each numbered slot is configurable as either 24-bit S/PDIF or 24-bit I2S (left-justified, MSB-first). I2S slots share a common BCK/LRCLK clock pair (LRCLK is always BCK + 1 due to a PIO side-set constraint). An optional master clock (MCK) at 128× or 256× Fs can be routed to a separate GPIO. PDM subwoofer is always on its own dedicated output and pin.

Hardware Setup

Flashing the Firmware

  1. Download the latest DSPi.uf2 release for your board.
  2. Hold the BOOTSEL button on your Pico while plugging it into your computer.
  3. A drive named RPI-RP2 will appear.
  4. Drag and drop the .uf2 file onto this drive.
  5. The Pico will reboot and appear as a “Weeb Labs DSPi” audio device.
  6. Download and launch the DSPi Console application to control the DSPi.

Wiring Guide

RP2350 (Pico 2) — up to 8 output pins:

FunctionPinConnection
Output Slot 0 (Out 1-2)GPIO 6 (default)S/PDIF or I2S data for main L/R or multi-way pair 1
Output Slot 1 (Out 3-4)GPIO 7 (default)S/PDIF or I2S data for multi-way pair 2
Output Slot 2 (Out 5-6)GPIO 8 (default)S/PDIF or I2S data for multi-way pair 3
Output Slot 3 (Out 7-8)GPIO 9 (default)S/PDIF or I2S data for multi-way pair 4
Subwoofer Out (PDM, Out 9)GPIO 10 (default)Active subwoofer or PDM-to-analog filter
I2S BCK (shared, I2S only)GPIO 14 (default)Bit clock for any slot configured as I2S
I2S LRCLK (I2S only)GPIO 15 (BCK + 1, fixed)Word/frame clock; always BCK + 1
I2S MCK (optional)GPIO 13 (default)128× or 256× Fs master clock when MCK is enabled
USBMicro-USBHost device (PC/Mac/Mobile Device)

RP2040 (Pico) — up to 6 output pins:

FunctionPinConnection
Output Slot 0 (Out 1-2)GPIO 6 (default)S/PDIF or I2S data for main L/R or stereo pair 1
Output Slot 1 (Out 3-4)GPIO 7 (default)S/PDIF or I2S data for stereo pair 2
Subwoofer Out (PDM, Out 5)GPIO 10 (default)Active subwoofer or PDM-to-analog filter
I2S BCK (shared, I2S only)GPIO 14 (default)Bit clock for any slot configured as I2S
I2S LRCLK (I2S only)GPIO 15 (BCK + 1, fixed)Word/frame clock; always BCK + 1
I2S MCK (optional)GPIO 13 (default)128× or 256× Fs master clock when MCK is enabled
USBMicro-USBHost device (PC/Mac/Mobile Device)

Notes: S/PDIF output requires either a Toshiba TX179 optical transmitter or a simple resistor divider. I2S output is a standard 24-bit-in-32-bit left-justified frame — wires straight into most I2S DACs. PDM output is a 1-bit logic signal that requires a resistor and capacitor to form a low-pass filter for conversion to analog audio.

Custom Pin Assignments

All default pin assignments above work out of the box, but every output pin — including I2S BCK and MCK — can be reassigned at runtime through the DSPi Console application. No reflashing required. This is useful when designing custom PCBs or adapting to boards where the default GPIOs are inconvenient.

Pin assignments are saved to flash and restored automatically at boot. A few GPIOs are reserved and unavailable for output use: GPIO 12 (UART TX) and GPIOs 23-25 (power control and LED). LRCLK is always pinned to BCK + 1 due to a PIO side-set constraint.

Alt text Alt text


DSP Features

Matrix Mixer

The matrix mixer routes the USB stereo input to all output channels. RP2350 has a 2x9 matrix (9 outputs), RP2040 has a 2x5 matrix (5 outputs). Each crosspoint (input/output pair) has:

  • Enable/Disable: Route active or inactive.
  • Gain: -inf to +12 dB per crosspoint.
  • Phase Invert: Polarity flip for driver alignment.

Each output channel also has:

  • Enable: Disabled outputs skip all processing (EQ, delay, conversion) to save CPU.
  • Gain: Per-output gain (-inf to +12 dB).
  • Mute: Soft mute per output.
  • Delay: Per-output time alignment.

Output Availability: Core 1 is shared between the PDM subwoofer modulator and the EQ worker that processes higher-numbered S/PDIF outputs. PDM and EQ worker modes are mutually exclusive:

RP2350:

ModeAvailable OutputsCore 1 Usage
PDM enabled (Out 9 on)Out 1-2 (S/PDIF 1) + Out 9 (PDM)Delta-sigma modulator
PDM disabled (Out 9 off)Out 1-8 (S/PDIF 1-4)EQ worker for Out 3-8

RP2040:

ModeAvailable OutputsCore 1 Usage
PDM enabled (Out 5 on)Out 1-2 (S/PDIF 1) + Out 5 (PDM)Delta-sigma modulator
PDM disabled (Out 5 off)Out 1-4 (S/PDIF 1-2)EQ worker for Out 3-4

When the PDM subwoofer is active, Core 1 is fully dedicated to the delta-sigma modulator, so higher-numbered S/PDIF outputs are unavailable. When PDM is off, Core 1 runs as an EQ worker processing those outputs in parallel with Core 0.

Common Configurations (RP2350):

Use CaseRoutingMode
Stereo + SubL→Out1, R→Out2, L+R→Out9PDM on (3 outputs)
2-Way ActiveL→Out1(tweeter), L→Out3(woofer), R→Out2(tweeter), R→Out4(woofer)PDM off (4 outputs)
3-Way ActiveAs above, plus mid-range on Out5-6PDM off (6 outputs)
4-Way ActiveAs above, plus super-tweeter on Out7-8PDM off (8 outputs)

Common Configurations (RP2040):

Use CaseRoutingMode
StereoL→Out1, R→Out2PDM off (2 outputs)
Stereo + SubL→Out1, R→Out2, L+R→Out5PDM on (3 outputs)
2-Way ActiveL→Out1(tweeter), L→Out3(woofer), R→Out2(tweeter), R→Out4(woofer)PDM off (4 outputs)

Parametric Equalization

Each filter band supports 6 types:

TypeDescription
FlatBypass (no processing)
PeakingParametric bell filter
Low ShelfLow-frequency shelf
High ShelfHigh-frequency shelf
Low PassLow-pass filter
High PassHigh-pass filter

On RP2040, all filters use biquad IIR (Transposed Direct Form II) with Q28 fixed-point arithmetic. On RP2350, the firmware uses a hybrid SVF/biquad architecture: filters below Fs/7.5 (~6.4 kHz at 48 kHz) use the Cytomic SVF (linear trapezoid) topology for superior numerical accuracy at low frequencies, while higher frequencies use traditional TDF2 biquad. All filters have configurable frequency, Q factor, and gain. Flat filters are automatically bypassed for zero CPU overhead.

Channel Layout:

RP2350 (11 channels):

ChannelIndexEQ Bands
Master Left010
Master Right110
Output 1-8 (S/PDIF)2-910 each
Output 9 (PDM Sub)1010

RP2040 (7 channels):

ChannelIndexEQ Bands
Master Left010
Master Right110
Output 1-4 (S/PDIF)2-510 each
Output 5 (PDM Sub)610

Loudness Compensation

Based on the ISO 226:2003 equal-loudness contour standard. At low listening volumes, the human ear is less sensitive to bass and treble frequencies. Loudness compensation applies a volume-dependent EQ curve to maintain perceived tonal balance across all listening levels.

  • Reference SPL: Configurable (40-100 dB). Set this to the SPL where your system sounds tonally balanced at full volume.
  • Intensity: Adjustable from 0-200% of the standard ISO curve.
  • Implementation: Precomputed coefficient tables for all 91 volume steps, double-buffered for glitch-free updates.

Headphone Crossfeed

Implements Bauer Stereophonic-to-Binaural (BS2B) crossfeed with a complementary filter design that reduces unnatural stereo separation for headphone listening. Each channel receives a lowpass-filtered, time-delayed mix of the opposite channel, simulating the acoustic crossfeed that occurs with loudspeaker listening.

  • Complementary Design: Direct path = input - lowpass(input). Guarantees mono signals pass through at unity gain with no coloration.
  • Interaural Time Delay (ITD): First-order all-pass filter adds ~220us of delay to the crossfeed path, modeling sound traveling around the head for 60-degree stereo speaker placement. ITD can be independently enabled/disabled.
  • Presets:
PresetCutoffFeed LevelCharacter
Default700 Hz4.5 dBBalanced, most popular
Chu Moy700 Hz6.0 dBStronger spatial effect
Jan Meier650 Hz9.5 dBSubtle, natural
Custom500-2000 Hz0-15 dBUser-defined

Volume Leveller

A feedforward, stereo-linked, single-band RMS dynamic range compressor that maintains consistent perceived volume across content with varying loudness.

  • Upward compression: Boosts content below the threshold while leaving content above the threshold completely untouched. No makeup gain needed.
  • RMS-based detection: Tracks root-mean-square envelope, which correlates with perceived loudness better than peak detection.
  • Soft-knee: Gradual transition between full boost and unity gain for transparent, artifact-free behavior.
  • Stereo-linked: The louder of the two channels determines gain for both, preserving the stereo image.
  • Gain-reduction safety limiter: -6 dBFS ceiling enforced via gain reduction (instant attack, 100 ms release) rather than hard clipping. Rarely engages since loud content passes through at unity.
  • Optional 10 ms lookahead for smoother transitions.
  • Configurable: speed (attack/release), max-gain ceiling (cap on how much quiet content can be lifted), and gate threshold (below which the leveller stops boosting to avoid amplifying silence/noise).

The leveller sits at PASS 2.5 — after Master EQ, before crossfeed. Independent of Loudness Compensation; both can be enabled together without conflict.

Per-Channel Preamp

Each USB input channel (Left and Right) has an independent preamp gain in dB, applied at PASS 1 before any other processing. Useful for trimming channel imbalance, attenuating hot inputs ahead of EQ, or matching levels across sources. A legacy single-value command remains for backward compatibility (sets both channels to the same value).

Master Volume

A device-side output ceiling applied at the very end of the signal chain, independent of USB host volume.

  • Range: -128 to 0 dB. -128 is a sentinel for true silence (mute).
  • Independent of USB host volume: the two multiply together. The host slider operates within whatever range master volume permits.
  • Independent of DSP processing: loudness compensation, EQ, leveller, and crossfeed are all driven by the raw USB volume, not the master volume — their behavior is unchanged regardless of the master setting.
  • Two persistence modes (selectable at runtime, persists across reboots):
    • Mode 0 — Independent (default). Master volume is a stand-alone device setting. The app calls a save command to capture the current value into the directory; that value is applied at every subsequent boot. Preset save/load do not touch master volume — switching presets never moves the volume.
    • Mode 1 — With preset. Master volume is part of each preset. Saved with the preset, restored on preset load, like any other DSP parameter. Useful when different presets target speaker setups with different sensitivity / maximum-output requirements.
  • Default at first boot: -20 dB (configurable via MASTER_VOL_DEFAULT_DB in firmware).

I2S Output

Each output slot can be switched at runtime between S/PDIF (default) and I2S, independently per slot. A single device can drive a mix — e.g., slot 0 as I2S into a DAC chip, slot 1 as S/PDIF over Toslink to an external receiver, all from the same audio pipeline.

  • I2S format: 24-bit data, left-justified, MSB-first, 32-bit frames. Drop-in to most standard I2S DACs (PCM5102, ES9038Q2M, etc.).
  • Shared clocks: All I2S slots share a single BCK/LRCLK pair. LRCLK is always BCK + 1 (PIO side-set hardware constraint).
  • Optional MCK: When enabled, a 128× or 256× Fs master clock is generated on a configurable GPIO. Required by some DACs that don’t have an internal PLL. At 96 kHz, only 128× is selectable due to PIO clock-divisor limits.
  • Sample-aligned start: I2S slots can be brought up together so multiple DACs stay phase-locked.

The DSP pipeline is identical for both output types — only the final encoding differs (BMC/NRZI for S/PDIF vs. raw left-justified PCM for I2S).

Subwoofer PDM Output

The subwoofer output uses a high-performance software-defined delta-sigma modulator running on Core 1.

  • Modulation: 2nd-Order Delta-Sigma
  • Oversampling Ratio: 256x (12.288 MHz bit clock at 48 kHz)
  • Dither: TPDF (Triangular Probability Density Function) with noise shaping
  • DC Protection: Leaky integrator design preventing DC offset accumulation

The objective was to use as much of Core 1 as necessary to produce an output that could be used full-range while sounding perfectly fine, even if it will only be used to feed a subwoofer. This implementation is very stable and without pops, clicks or idle tones.


User Presets

DSPi includes a 10-slot preset system that stores complete DSP configurations in flash. A preset is always active — there is no “no preset” state.

  • 10 Preset Slots: Each slot stores the full DSP state: per-channel preamp, EQ bands, delays, loudness, leveller, crossfeed, matrix mixer, output gains/mutes, output type (S/PDIF or I2S), I2S clock configuration, pin assignments, master volume (used in Mode 1), and per-channel names.
  • Per-Channel Names: Each channel can be given a user-defined name (up to 31 characters) that is stored with the preset.
  • Startup Configuration: Choose which preset loads on boot — either a specific default slot or whichever slot was last active.
  • Pin Config Inclusion: Optionally include or exclude GPIO pin assignments when saving/loading presets (default: include — pin layout travels with the preset).
  • Master Volume Mode: Selects whether master volume is part of each preset (Mode 1) or stored independently in the preset directory (Mode 0, default). See Master Volume.
  • Preset-Switch Mute: Audio output is briefly muted (~10 ms) during preset transitions to prevent audible glitches.
  • Legacy Commands: The original save/load/reset commands (0x51-0x53) redirect through the preset system, operating on the currently active slot.
  • Bulk Parameter Transfer: The complete DSP state can be read or written in a single USB control transfer (~2.9 KB) for fast synchronization with host applications.
  • Auto-Migration: Older preset directories are upgraded transparently on first boot of new firmware — slot names, startup config, and other persisted state are preserved.

Developer Reference

System Architecture

  • Core 0: USB communication, audio streaming, DSP processing (master EQ, crossfeed, loudness, matrix mixing, output EQ for S/PDIF pair 1), and control logic.
  • Core 1 (three modes):
    • PDM Mode: Delta-sigma modulator for subwoofer output (when the PDM output is enabled).
    • EQ Worker Mode: Processes output EQ, delay, and S/PDIF conversion for higher-numbered outputs in parallel with Core 0. On RP2350: outputs 3-8. On RP2040: outputs 3-4. Activated when any of those outputs are enabled and PDM is disabled.
    • Idle Mode: When no outputs requiring Core 1 are enabled.
  • PIO & DMA: Hardware offloading for S/PDIF encoding (PIO0) and PDM bitstream generation (PIO1) ensures zero CPU overhead for I/O.
  • Math Engine:
    • RP2040: 32-bit fixed-point (Q28) processing with hand-optimized ARM assembly for the inner DSP loop.
    • RP2350: Single-precision float pipeline with hardware FPU. Hybrid SVF/biquad EQ — Cytomic SVF for low frequencies (below Fs/7.5), TDF2 biquad above. SVF provides superior numerical accuracy for low-frequency filters where biquad coefficient quantization becomes problematic.

Note: PDM mode and EQ Worker mode are mutually exclusive on Core 1. When the PDM output is enabled, Core 0 handles all S/PDIF output EQ processing. When PDM is disabled and higher-numbered outputs are active, Core 1 runs as an EQ worker for those outputs.

Performance Tuning

Both platforms run at a fixed 307.2 MHz system clock (VCO 1536 MHz / 5 / 1) so PIO dividers stay integer at every supported sample rate, eliminating sample-rate-dependent clock switching glitches.

PlatformSystem ClockCore Voltage
RP2040307.2 MHz (overclock)1.15 V
RP2350307.2 MHz1.15 V

The RP2040 reaches 307.2 MHz with a slight voltage bump above the 1.10 V nominal; the RP2350 is comfortable at the same voltage at this clock. The voltage step is applied before the frequency change. Sample rate changes do not retune the system clock, only the PIO dividers, so transitions between 44.1 / 48 / 96 kHz are seamless.

Flash access is also tuned: PICO_FLASH_SPI_CLKDIV is set to 6 to keep XIP and erase/program operations safely below the W25Q080’s 104–133 MHz spec at this clock. On the RP2350, runtime QMI register management is handled by firmware/DSPi/flash_clkdiv.c since the bootrom does not honor the boot2 setting on that platform.

USB Control Protocol

Configuration is performed via Interface 2 (Vendor Interface) using Control Transfers under Windows and via Interface 0 under macOS. The device supports WinUSB/WCID for automatic driverless installation on Windows.

Request Table

CodeNameDirectionPayloadDescription
0x42REQ_SET_EQ_PARAMOUT16 bytesUpload filter parameters
0x43REQ_GET_EQ_PARAMIN16 bytesRead filter parameters
0x44REQ_SET_PREAMPOUT4 bytesSet global gain (float dB)
0x45REQ_GET_PREAMPIN4 bytesGet global gain
0x46REQ_SET_BYPASSOUT1 byteBypass Master EQ (1=On, 0=Off)
0x47REQ_GET_BYPASSIN1 byteGet bypass state
0x48REQ_SET_DELAYOUT4 bytesSet channel delay (float ms)
0x49REQ_GET_DELAYIN4 bytesGet channel delay
0x50REQ_GET_STATUSIN4-12 bytesGet system statistics (wValue selects field)
0x51REQ_SAVE_PARAMSIN1 byteSave to active preset slot
0x52REQ_LOAD_PARAMSIN1 byteReload active preset slot
0x53REQ_FACTORY_RESETIN1 byteReset live state to defaults
0x54REQ_SET_CHANNEL_GAINOUT4 bytesSet output channel gain (float dB)
0x55REQ_GET_CHANNEL_GAININ4 bytesGet output channel gain
0x56REQ_SET_CHANNEL_MUTEOUT1 byteMute output channel (1=Muted)
0x57REQ_GET_CHANNEL_MUTEIN1 byteGet mute state
0x58REQ_SET_LOUDNESSOUT1 byteEnable/disable loudness (1=On)
0x59REQ_GET_LOUDNESSIN1 byteGet loudness state
0x5AREQ_SET_LOUDNESS_REFOUT4 bytesSet reference SPL (float, 40-100)
0x5BREQ_GET_LOUDNESS_REFIN4 bytesGet reference SPL
0x5CREQ_SET_LOUDNESS_INTENSITYOUT4 bytesSet intensity % (float, 0-200)
0x5DREQ_GET_LOUDNESS_INTENSITYIN4 bytesGet intensity
0x5EREQ_SET_CROSSFEEDOUT1 byteEnable/disable crossfeed (1=On)
0x5FREQ_GET_CROSSFEEDIN1 byteGet crossfeed state
0x60REQ_SET_CROSSFEED_PRESETOUT1 byteSet preset (0-3)
0x61REQ_GET_CROSSFEED_PRESETIN1 byteGet current preset
0x62REQ_SET_CROSSFEED_FREQOUT4 bytesSet custom frequency (float Hz, 500-2000)
0x63REQ_GET_CROSSFEED_FREQIN4 bytesGet custom frequency
0x64REQ_SET_CROSSFEED_FEEDOUT4 bytesSet custom feed level (float dB, 0-15)
0x65REQ_GET_CROSSFEED_FEEDIN4 bytesGet custom feed level
0x66REQ_SET_CROSSFEED_ITDOUT1 byteEnable/disable ITD (1=On)
0x67REQ_GET_CROSSFEED_ITDIN1 byteGet ITD state
0x70REQ_SET_MATRIX_ROUTEOUT8 bytesSet matrix crosspoint (MatrixRoutePacket)
0x71REQ_GET_MATRIX_ROUTEIN8 bytesGet matrix crosspoint
0x72REQ_SET_OUTPUT_ENABLEOUT1 byteEnable/disable output channel
0x73REQ_GET_OUTPUT_ENABLEIN1 byteGet output enable state
0x74REQ_SET_OUTPUT_GAINOUT4 bytesSet per-output gain (float dB)
0x75REQ_GET_OUTPUT_GAININ4 bytesGet per-output gain
0x76REQ_SET_OUTPUT_MUTEOUT1 byteMute output (1=Muted)
0x77REQ_GET_OUTPUT_MUTEIN1 byteGet output mute state
0x78REQ_SET_OUTPUT_DELAYOUT4 bytesSet per-output delay (float ms)
0x79REQ_GET_OUTPUT_DELAYIN4 bytesGet per-output delay
0x7AREQ_GET_CORE1_MODEIN1 byteGet Core 1 mode (0=Idle, 1=PDM, 2=EQ Worker)
0x7BREQ_GET_CORE1_CONFLICTIN1 byteCheck if PDM vs EQ Worker conflict exists
0x7CREQ_SET_OUTPUT_PININ1 byteChange output GPIO pin (returns status)
0x7DREQ_GET_OUTPUT_PININ1 byteGet current GPIO pin for an output
0x7EREQ_GET_SERIALINvariableGet unique board serial number
0x7FREQ_GET_PLATFORMIN1 byteGet platform ID (0=RP2040, 1=RP2350)
0x83REQ_CLEAR_CLIPSOUTClear clip detection latches
0x90REQ_PRESET_SAVEIN1 byteSave live state to preset slot (wValue=slot)
0x91REQ_PRESET_LOADIN1 byteLoad preset slot to live state (wValue=slot)
0x92REQ_PRESET_DELETEIN1 byteDelete preset slot (wValue=slot)
0x93REQ_PRESET_GET_NAMEIN32 bytesGet preset name (wValue=slot)
0x94REQ_PRESET_SET_NAMEOUT32 bytesSet preset name (wValue=slot)
0x95REQ_PRESET_GET_DIRINvariableGet preset directory (occupancy, startup config)
0x96REQ_PRESET_SET_STARTUPOUT2 bytesSet startup mode and default slot
0x97REQ_PRESET_GET_STARTUPIN2 bytesGet startup configuration
0x98REQ_PRESET_SET_INCLUDE_PINSOUT1 byteSet pin config inclusion (1=include)
0x99REQ_PRESET_GET_INCLUDE_PINSIN1 byteGet pin config inclusion setting
0x9AREQ_PRESET_GET_ACTIVEIN1 byteGet currently active preset slot index
0x9BREQ_SET_CHANNEL_NAMEOUT32 bytesSet channel name (wValue=channel)
0x9CREQ_GET_CHANNEL_NAMEIN32 bytesGet channel name (wValue=channel)
0xA0REQ_GET_ALL_PARAMSIN~2896 bytesBulk read entire DSP state (multi-packet)
0xA1REQ_SET_ALL_PARAMSOUT~2896 bytesBulk write entire DSP state (multi-packet)
0xB0REQ_GET_BUFFER_STATSINvariableRead buffer fill statistics
0xB1REQ_RESET_BUFFER_STATSIN1 byteReset buffer statistics counters
0xB2REQ_GET_USB_ERROR_STATSIN24 bytesRead USB PHY error counters (CRC/bit-stuff/timeout/overflow/seq)
0xB3REQ_RESET_USB_ERROR_STATSIN1 byteReset USB PHY error counters
0xB4REQ_SET_LEVELLER_ENABLEOUT1 byteEnable/disable Volume Leveller
0xB5REQ_GET_LEVELLER_ENABLEIN1 byteGet leveller enable state
0xB6REQ_SET_LEVELLER_AMOUNTOUT4 bytesSet leveller target/amount (float)
0xB7REQ_GET_LEVELLER_AMOUNTIN4 bytesGet leveller amount
0xB8REQ_SET_LEVELLER_SPEEDOUT1 byteSet leveller attack/release speed
0xB9REQ_GET_LEVELLER_SPEEDIN1 byteGet leveller speed
0xBAREQ_SET_LEVELLER_MAX_GAINOUT4 bytesSet max upward gain (float dB)
0xBBREQ_GET_LEVELLER_MAX_GAININ4 bytesGet max upward gain
0xBCREQ_SET_LEVELLER_LOOKAHEADOUT1 byteEnable/disable 10 ms lookahead
0xBDREQ_GET_LEVELLER_LOOKAHEADIN1 byteGet lookahead state
0xBEREQ_SET_LEVELLER_GATEOUT4 bytesSet noise-gate threshold (float dB)
0xBFREQ_GET_LEVELLER_GATEIN4 bytesGet noise-gate threshold
0xC0REQ_SET_OUTPUT_TYPEOUT1 byteSet slot output type (0=S/PDIF, 1=I2S; wValue=slot)
0xC1REQ_GET_OUTPUT_TYPEIN1 byteGet slot output type (wValue=slot)
0xC2REQ_SET_I2S_BCK_PINOUT1 byteSet shared I2S BCK GPIO (LRCLK auto = BCK + 1)
0xC3REQ_GET_I2S_BCK_PININ1 byteGet current I2S BCK pin
0xC4REQ_SET_MCK_ENABLEOUT1 byteEnable/disable I2S master clock output
0xC5REQ_GET_MCK_ENABLEIN1 byteGet MCK enable state
0xC6REQ_SET_MCK_PINOUT1 byteSet MCK GPIO
0xC7REQ_GET_MCK_PININ1 byteGet MCK GPIO
0xC8REQ_SET_MCK_MULTIPLIEROUT1 byteSet MCK multiplier (0=128×, 1=256×)
0xC9REQ_GET_MCK_MULTIPLIERIN1 byteGet MCK multiplier
0xD0REQ_SET_PREAMP_CHOUT4 bytesSet per-channel preamp (wValue=channel, payload=float dB)
0xD1REQ_GET_PREAMP_CHIN4 bytesGet per-channel preamp (wValue=channel)
0xD2REQ_SET_MASTER_VOLUMEOUT4 bytesSet master volume (-128 mute sentinel, -127..0 dB)
0xD3REQ_GET_MASTER_VOLUMEIN4 bytesGet current live master volume
0xD4REQ_SET_MASTER_VOLUME_MODEOUT1 byteSet persistence mode (0=independent, 1=with preset)
0xD5REQ_GET_MASTER_VOLUME_MODEIN1 byteGet persistence mode
0xD6REQ_SAVE_MASTER_VOLUMEIN1 byteSave live master volume to directory (mode 0 persistence)
0xD7REQ_GET_SAVED_MASTER_VOLUMEIN4 bytesRead directory’s saved master-volume value
0xF0REQ_ENTER_BOOTLOADERIN1 byteReboot into UF2 bootloader for firmware update

REQ_GET_STATUS (0x50) - System Telemetry

The REQ_GET_STATUS request returns data based on the wValue field:

wValueReturnsDescription
0uint32Peaks for channels 0-1 (packed 16-bit values)
1uint32Peaks for channels 2-3 (packed 16-bit values)
2uint32Peak for channel 4 + CPU0/CPU1 load (packed)
3uint32PDM ring buffer overruns
4uint32PDM ring buffer underruns
5uint32PDM DMA overruns
6uint32PDM DMA underruns
7uint32S/PDIF overruns
8uint32S/PDIF underruns
912 bytesCombined: all 5 peaks + CPU loads
10uint32USB audio packet count
11uint32USB alt setting
12uint32USB audio mounted state
13uint32System clock frequency (Hz)
14uint32Core voltage (millivolts)
15uint32Sample rate (Hz)
16int32System temperature (centi-degrees C)
17uint32Total S/PDIF DMA starvations (all slots combined)
18uint32S/PDIF slot 0 starvations (Out 1-2)
19uint32S/PDIF slot 1 starvations (Out 3-4)
20uint32S/PDIF slot 2 starvations (Out 5-6, RP2350)
21uint32S/PDIF slot 3 starvations (Out 7-8, RP2350)

A starvation event means the S/PDIF DMA needed a buffer but the consumer pool was empty, so the firmware substituted a silence buffer for that transfer. This is a more direct output-side dropout signal than the older spdif_underruns USB-packet-gap heuristic.

Data Structures

Filter Packet (16 bytes):

struct __attribute__((packed)) {
    uint8_t channel;  // RP2350: 0-10, RP2040: 0-6
    uint8_t band;     // 0-9
    uint8_t type;     // 0=Flat, 1=Peak, 2=LS, 3=HS, 4=LP, 5=HP
    uint8_t reserved;
    float freq;       // Hz
    float Q;
    float gain_db;
}

Matrix Route Packet (8 bytes):

struct __attribute__((packed)) {
    uint8_t input;          // 0-1 (USB L/R)
    uint8_t output;         // RP2350: 0-8, RP2040: 0-4
    uint8_t enabled;        // 0 or 1
    uint8_t phase_invert;   // 0 or 1
    float gain_db;          // -inf to +12dB
}

Runtime Pin Configuration

Output GPIO pins can be reassigned at runtime without reflashing. This is useful for custom PCB layouts or when the default pin assignments conflict with other hardware.

REQ_SET_OUTPUT_PIN (0x7C) — IN transfer, returns 1-byte status:

  • wValue = (new_pin << 8) | output_index
  • RP2350: output_index 0-3 for S/PDIF outputs 1-4, 4 for PDM subwoofer
  • RP2040: output_index 0-1 for S/PDIF outputs 1-2, 2 for PDM subwoofer
  • S/PDIF outputs are automatically disabled and re-enabled during the pin change (~1ms audio dropout on that output only)
  • PDM output must be disabled first (disable via REQ_SET_OUTPUT_ENABLE), otherwise returns PIN_CONFIG_OUTPUT_ACTIVE
Status CodeValueMeaning
PIN_CONFIG_SUCCESS0x00Pin changed successfully
PIN_CONFIG_INVALID_PIN0x01Pin out of range or reserved (GPIO 12, 23-25)
PIN_CONFIG_PIN_IN_USE0x02Pin already assigned to another output
PIN_CONFIG_INVALID_OUTPUT0x03Output index out of range
PIN_CONFIG_OUTPUT_ACTIVE0x04PDM output must be disabled before changing its pin

REQ_GET_OUTPUT_PIN (0x7D) — IN transfer, returns 1 byte:

  • wValue = output_index
  • Returns the current GPIO pin number for that output

Pin assignments are stored in each preset and can optionally be included during preset save/load (controlled via REQ_PRESET_SET_INCLUDE_PINS).


Building from Source

To build the firmware yourself, you’ll need a standard Raspberry Pi Pico C/C++ development environment.

1. Install Prerequisites

Ensure you have the following tools installed:

  • CMake (3.13 or newer)
  • Arm GNU Toolchain (arm-none-eabi-gcc, etc.)
  • Python 3 (for Pico SDK scripts)
  • Git

2. Clone the Repository

Clone the project recursively to include the Pico SDK and other submodules:

git clone --recursive https://github.com/WeebLabs/DSPi.git
cd DSPi

If you already cloned without --recursive, run:

git submodule update --init --recursive

3. Build the Firmware

You can build for either the standard RP2040 (Raspberry Pi Pico) or the newer RP2350 (Raspberry Pi Pico 2). The build system uses separate directories to avoid conflicts.

Option A: Build for RP2040 (Standard Pico)

mkdir build-rp2040
cd build-rp2040
cmake -DPICO_BOARD=pico -DPICO_EXTRAS_PATH=../firmware/pico-extras ../firmware
make

Output: DSPi/DSPi.uf2

Option B: Build for RP2350 (Pico 2)

mkdir build-rp2350
cd build-rp2350
cmake -DPICO_BOARD=pico2 -DPICO_EXTRAS_PATH=../firmware/pico-extras ../firmware
make

Output: DSPi/DSPi.uf2

4. Flash the Device

  1. Hold the BOOTSEL button on your board while plugging it in.
  2. Drag and drop the generated .uf2 file onto the RPI-RP2 (or RP2350) drive.

Alternatively, an already-running DSPi can be put into bootloader mode without a button press by sending REQ_ENTER_BOOTLOADER (0xF0). The DSPi Console application uses this for one-click firmware updates. See Documentation/Features/firmware_update.md for the protocol details.


Detailed Specifications

In-depth specs for each subsystem are kept under Documentation/Features/. These are the authoritative source for protocol formats, wire layouts, edge cases, and host-app integration patterns.

FeatureSpec
Matrix Mixermatrixmixer_spec.md
User Presetsuser_presets_spec.md
Master Volumemaster_volume_spec.md
Per-Channel Preampper_channel_preamp_spec.md
Volume Levellervolume_leveller_spec.md
I2S Outputi2s_output_spec.md
Peak / Clip Meteringpeak_clip_metering_spec.md
Buffer Statisticsbuffer_statistics_spec.md
S/PDIF DMA Starvationspdif_starvation_spec.md
USB Error Diagnosticsusb_errors_spec.md
Core 1 Modescore1_modes_spec.md
Device Identificationdevice_identification_spec.md
S/PDIF Input (planned)SPDIF_input_spec.md
Firmware Update via USBDocumentation/Features/firmware_update.md
Roadmaproadmap.md

License

This project is licensed under the GNU General Public License v3.0. See LICENSE for details.

Similar Articles

can1357/oh-my-pi

GitHub Trending (daily)

Oh My Pi is an open-source coding agent built on Pi, offering a wired-in IDE, support for 40+ providers, built-in tools, and significant performance improvements across models.

Raspberry Pi Pico W as USB Wi-Fi Adapter

Hacker News Top

A project that turns the Raspberry Pi Pico W into a USB Wi-Fi adapter, providing a simple way to add wireless connectivity to devices via USB.