Skip to content

Stereo audio panning with audiofilters.Filter #11024

@relic-se

Description

@relic-se

CircuitPython version and board name

Adafruit CircuitPython 10.3.0-alpha.2 on 2026-05-15; Raspberry Pi Pico 2 with rp2350a

Code/REPL

import audiobusio
import audiomixer
import board
import synthio
import time

FILTER = True
FILTER_BYPASS = False

audio = audiobusio.I2SOut(
    bit_clock=board.GP20,
    word_select=board.GP21,
    data=board.GP22,
)

synth = synthio.Synthesizer(
    sample_rate=44100,
    channel_count=2,
)

if FILTER:
    import audiofilters

    effect_filter = audiofilters.Filter(
        sample_rate=44100,
        channel_count=2,
        filter=None if FILTER_BYPASS else synthio.Biquad(
            mode=synthio.FilterMode.LOW_PASS,
            frequency=44100 / 2,
        ),
    )
    audio.play(effect_filter.play(synth))

else:
    audio.play(synth)

synth.press(synthio.Note(
    frequency=220,
    panning=synthio.LFO(
        rate=0.25,
    ),
    amplitude=0.5,
))

Behavior

Image

The stereo waveform graph above adheres to the following settings:

# Recording 1
FILTER = False
FILTER_BYPASS = True

# Recording 2
FILTER = True
FILTER_BYPASS = True

# Recording 3
FILTER = True
FILTER_BYPASS = False

Stereo audio data behaves normally until a synthio.Biquad object is provided which negatively impacts stereo imaging.

Description

A single biquad_filter_state struct is being created within filter_states for each synthio.Biquad assigned to the filter list in shared-module/audiofilters/Filter.c. This only accounts for a single channel of audio data, so each state is being shared across both left and right channels.

synthio_biquad_filter_samples(filter_obj, &self->filter_states[j], self->filter_buffer, n_samples);

To fix this if channel_count=2, two biquad_filter_state structs should be created per synthio.Biquad and they should be used with synthio_biquad_filter_samples depending on the current channel of the sample.

Additional information

I wrote this module, so it's on me 😆

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions