"Open the output stream once, at startup. Hold it for the life of the process. Every request enqueues; none opens its own."
Born With the App, Not With a Request
The audio worker is created in the application's lifespan — the block that runs once at startup and once at shutdown. On start it opens a single persistent output stream and holds it. On shutdown it drains its queue and closes the stream cleanly. In between, for the entire life of the process, that one worker owns the device. Requests don't open streams; they enqueue audio onto the worker, and playback is serialized through it.
Why Not Open a Stream Per Request
Opening the device per request seems simpler until two requests overlap. Now two streams contend for one output, playback races, and the "who has the speaker" question has no stable answer. A single long-lived worker turns concurrency into a queue: requests line up, the worker plays them in order, and there's never a moment where two owners fight over the device. The persistence is the point — the stream outlives every individual request.
Keep Heavy Work Off the Event Loop
There's a subtlety that bites async services: the worker's audio path uses blocking, CPU-heavy tools — decoding, probing, muxing with ffmpeg. Run those directly on the event loop and they freeze every other request while they churn. Bellows runs ffmpeg and ffprobe off the loop, in an executor, so the async server stays responsive. And because startup and shutdown are explicit, an interrupted job can be healed when the process comes back up, rather than left dangling.