Skip to content

v0.4.0 — the audio format contract

Choose a tag to compare

Breaking: the audio roles are renamed, and audio_in is removed.

An app can now choose its audio format at runtime on every board, through one contract:

pcm = board_peripherals.pcm_out(AudioFormat(44100, 2, 16), latency="low")

One name, one return type

pcm_out returns a PCMOutput, pcm_in a PCMInput, audio_out an AudioOut sample player — on boards and hosts alike. Previously audiodev.auto.audio_out returned a raw transport while board_peripherals.audio_out returned a player, so an app that could reach either factory could not know what it held and wrote getattr(pcm, "transport", pcm).

There is deliberately no audio_in: output has a player layer above raw PCM, capture does not.

pcm_out also needs no audioif in firmware, since nothing pulls a sample graph — which is what lets a headless Spotify Connect speaker or USB sound card run on a build with no DSP package in it.

Boards declare, audiodev decides

Boards publish an AudioCapability; audiodev.negotiate() holds the policy and raises rather than substituting. channels (what the wire opens) is separate from native_channels (what reaches a transducer) — the ESP32-P4's ES8311 clocks two slots into one speaker, so 44.1 kHz stereo needs no mixdown at all.

I2SWire publishes the port and pin numbers, so a consumer that drives the peripheral itself stops reaching into board-private names.

Verified on hardware

Three ESP32 families: ESP32-P4 by ear at 24 kHz mono and 44.1 kHz stereo; LilyGO T-Embed acoustically through its own microphones; QT Py ESP32 Pico + Adafruit Audio BFF by ear. Rates 8 kHz–48 kHz within 1 permille. USB audio end to end at 48 kHz stereo: 191,986 B/s against 192,000 nominal, zero timeouts.

Upgrading

was now
audio_out() for a raw transport pcm_out()
audio_out().transport pcm_out()
audio_in() pcm_in()
audiodev.auto.audio_out audiodev.auto.pcm_out
audiodev.auto.sample_audio_out audiodev.auto.audio_out
audiodev.auto.AutoAudio removed
pwm_tone.audio_out pwm_tone.tone_out

Board audio roles are factories — board_config.audio_out needs parentheses.

Known gap: no hardware here has a stereo transducer, so channel order is untested (#34). See CHANGELOG.md for the full list.