v0.4.0 — the audio format contract
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.