Skip to content

Multiroom

foonerd edited this page Oct 21, 2026 · 3 revisions

Multiroom

The player is a room of an Evo multiroom group: an Evo device elected as the group's source-host sends the room's audio over the network, and this player plays it in step with the other rooms. Nothing is configured on the card for it; the device announces itself and the group's source-host finds it.

How a room joins

With the network up the player announces itself on the local network as _evo._tcp (mDNS), the same service every Evo device announces, with its Evo id in the record: a UUID derived from the chip, the same across boots, cards and reflashes. The Evo id row at the bottom of Settings shows it. On the Evo device that runs the group, add this id to the group as a member; the source-host dials the player on port 7331, and when the group plays, the player plays.

While a source-host is sending, the player's face shows the room: the title line says Evo multiroom, the artist line names the source-host and the album line the group, the status strip says Evo room, and the position counts from the moment the room began. The art square shows the name card. Whatever the queue was playing is held: when the source-host says goodbye, drops the connection or falls silent for five seconds, the room ends and the queue plays again from the start of the track it was on.

The controls while a room plays

Control Does
Play / pause Holds the room and resumes it; what arrived while held is dropped, so the room is back in step when it resumes rather than behind.
Next, Previous Give the sink back to the queue: the room ends, the queue moves as asked, and this connection from the source-host is not granted the sink again. A new connection (the source-host restarting, or the group's source-host changing) is.
Volume The player's own, as always.

A room cannot be seeked.

Timing

Each frame the source-host sends is stamped with the source-host's clock. The player plays a frame 200 ms behind its stamp, anchored on the first frame's arrival, which is what Evo's Linux receiver does, so a Linux room and this room land together. The player answers the source-host's clock probes every five seconds; the offset it measures is reported but not yet applied to the schedule, as on the Linux receiver.

What crosses the wire

PCM, 48 kHz, 16-bit stereo, 20 ms a frame, 192 KB/s. The player offers the plane's binary frame format (bin1) in its handshake; a source-host of this version sends it, an older one sends the JSON envelope and the player decodes that too. The device never claims to be a source-host and never dials: the Evo election picks a group's source-host as the lowest device id among the members, and this player's id begins with f so it never wins that.

What it costs the board

About 4.5 KB of internal SRAM at idle for the listener and the announcement, and the frames in PSRAM while a room plays. The mDNS responder runs in PSRAM.

Snapcast

The player is also a Snapcast client: a snapserver on the network streams the same audio to every client with the server's clock on each piece, and every client plays it at the same moment. Anything that feeds a snapserver reaches the player: a music server with a Snapcast output, a home automation hub, a snapserver of your own fed from a pipe.

How the player finds the server

Nothing on the card is needed when the server announces itself (_snapcast._tcp, which snapserver does through Avahi): once the network is up the player asks for it, dials the first that answers on port 1704 and says hello as evo-player (or the name on the card), with the chip's MAC as its client id, which is how the server's client list shows it. With no answer it asks again every thirty seconds. A server that does not announce itself, or one of several to prefer, goes on the card:

snapserver = music.local
snapserver = 192.0.2.5:1704

The port is 1704 when left out. The player stays connected to the server for as long as it runs; a server that goes away is dialled again, with a lengthening pause, and a server the player found on the network and then lost is looked for again in case it is back at another address.

What plays

The server decides. A snapserver with no audio flowing sends nothing and the card plays as usual. The moment the stream carries audio the player takes the sink from the queue and plays the stream: the face says Snapcast, the title line names the stream as the server named it, the artist line the server, the album line Snapcast, and the position counts from the hand-over. When the stream falls silent for five seconds, or the server closes the connection, the stream ends and the queue plays again from the start of the track it was on.

The server's volume and mute for this client apply, on a square curve (half on the server's slider is a quarter of the level), under the player's own volume. The controls are the room's: Play/pause holds and resumes in step, Next and Previous give the sink back to the queue and this connection from the server is not granted it again (a new one is).

Timing

Snapcast works the other way round from the Evo room: the server stamps each piece with its own clock and tells the client how far behind the stamp to play (the server's buffer, 1000 ms by default, less the latency the server sets for this client). The player probes the server's clock ten times at the start, then every second, and keeps the median of the last fifteen answers as the offset between the clocks; on the bench the offset holds to within a few microseconds from one minute to the next. Each piece is written to the sink when the sound already written will have played out at the piece's moment: an early piece waits, or has the server's silence written ahead of it; a steadily late or early run of pieces has a frame in fifty dropped or doubled until the clocks meet; one well behind has its front trimmed; one more than 200 ms late is dropped. The sink's account of what it holds is exact while sound flows and anchored to one DMA buffer (21 ms at 48 kHz) when a stream starts into an empty sink, which bounds how far this room can sit from another. The decoded pieces wait in PSRAM; the server's buffer sets how many.

What crosses the wire

FLAC (the server's default) or PCM, at the stream's rate and depth; 48 kHz 16-bit stereo is the common case. FLAC is decoded frame by frame as it arrives, on the chip, through Symphonia. Opus and Vorbis streams are refused with a line in the log; set the stream's codec=flac or codec=pcm on the server.

Volumio

The player is a device in Volumio's Multiroom. A Volumio on the same network finds it by itself, lists it by name with what it is playing and its cover, and can put it into a group from Volumio's own screen: the player then plays the group's stream, in time with the other members, until Volumio takes it out again. Nothing is configured on the player for this.

What Volumio shows

The player's name (the name key, evo-player when not set), its state and the track it is on, and a cover: the cover of what plays when the album folder has one, else the evo tile. There is no volume slider for the player in Volumio: the volume is the player's own, set on its panel, as for any device that keeps its volume to itself. Volumio polls the player for its state when it connects and is told of every change after that.

Grouping

In Volumio's Multiroom, pick the player as an output of a group the way any device is picked. Volumio tells the player which server to play from and the player's Snapcast receiver dials it; the face says Snapcast and the group's stream plays as described above. Volumio's word outranks the snapserver key and the network search while the player is grouped; taken out of the group, the player goes back to the card's server, or to the search, and the queue plays again when the stream stops. A player grouped by a Volumio that then goes away stays on that server until told otherwise, as a Volumio device would.

How it works

Volumio devices find each other by mDNS (_Volumio._tcp on port 3000) and talk socket.io to each other. The player announces itself the same way and speaks the same protocol on its HTTP long-polling transport: a Volumio that finds the player connects to it, introduces itself and asks for the player's state and output, and the player keeps a session back to that Volumio so that a group leader's instructions, which Volumio sends to everything connected to it, reach the player too. Several Volumio devices may be connected at once; each one lists the player.

The player does not go looking for Volumio devices itself, and this is deliberate: a device that asks the network for a service it also advertises is, to the mDNS responder Volumio runs, a sign that its records of that device are stale, and it drops them within two minutes. The player lets itself be found instead.

Clone this wiki locally