Multi-Site Setup

A single shiloh-mixer instance supports multiple relay clients simultaneously. Each
client subscribes to exactly one feed (main, monitor, or cue) and the mixer fans out
independently to each.


Running Multiple Pi Relays from One Mixer

Start each relay with a unique --name. The mixer tracks relay sessions by name and
persists feed assignments between restarts.

# Pi at stage left — main feed
shiloh-relay connect \
    --server stg-srv001:5005 \
    --name stage-left \
    --device "hw:0,0" \
    --buffer-ms 30

# Pi at front-of-house — main feed, higher buffer for wired Ethernet drop
shiloh-relay connect \
    --server stg-srv001:5005 \
    --name foh \
    --device "hw:1,0" \
    --buffer-ms 50

# Laptop in the monitor position — monitor feed
shiloh-relay connect \
    --server stg-srv001:5005 \
    --name monitor-mix \
    --buffer-ms 30

Each relay connects, registers, and receives its assigned feed independently. The mixer
sends a separate UDP stream to each client address. There is no multicast — bandwidth
scales linearly with client count (~1.6 Mbit/s per stereo client at 48 kHz / 128 frames).


Per-Bus Relay Assignment

Each relay client is assigned to one feed. Assignments persist by name in
~/mixer-state/relay-assignments.json.

Assign from the web UI

In mixer_web at the sessions panel, find the relay client row and click the feed
selector to switch between main / monitor / cue. The change is applied immediately
without disconnecting the client.

Assign via control plane (UDP JSON)

echo '{"op":"set_relay_assignment","name":"stage-left","feed":"main"}' \
    | nc -u 127.0.0.1 19997

Valid feed values: "main", "monitor", "cue".

Default assignment

If a client connects with a name that has no saved assignment, it defaults to the
main feed. You can change this by adjusting relay-assignments.json before the
client connects, or by reassigning from the UI after it appears.


Three-Bus Example

A typical live setup with three relay destinations:

Client name Feed Purpose
pi-main main FOH speakers (full mix, with mutes)
pi-monitor monitor Stage monitor wedges (mute-exempt mix)
pi-cue cue Individual channel cue for soundcheck
web-relay-main main Browser listen page for remote audience

In shiloh-mixer.toml, each channel has independent main_gain, monitor_gain, and
cue_gain faders, so the monitor and cue mixes can differ substantially from main.

[[channels]]
id           = "bcast1"
label        = "Studio"
kind         = "stereo"
main_gain    = 1.0
monitor_gain = 0.8    # slightly lower in monitors
cue_gain     = 1.2    # louder in cue for isolated listening

Redundancy Considerations

shiloh-relay has a built-in reconnect loop with exponential backoff. If the mixer
restarts:

  1. Relay sessions evict from the mixer after 5 s of no PING (the relay’s heartbeat interval
    is 1 s, so the server times out within five missed PINGs).
  2. The relay detects no audio for > 3 s (its internal watchdog), then reconnects.
  3. After reconnect, feed assignment is restored from relay-assignments.json.

Total reconnect window: typically 5–10 s after a mixer restart.

For critical applications (broadcast transmission), run shiloh-relay under systemd
with Restart=always and RestartSec=1:

[Service]
ExecStart=/home/pi/bin/shiloh-relay connect \
    --server 10.0.0.1:5005 --name pi-main
Restart=always
RestartSec=1

There is no active/standby failover to a second mixer instance. If the mixer host fails,
all relay clients reconnect when it recovers. Plan for maintenance windows during off-peak
times.


Bandwidth Planning

Clients Bandwidth from mixer (aggregate)
1 ~1.6 Mbit/s
4 ~6.4 Mbit/s
8 ~12.8 Mbit/s
16 ~25.6 Mbit/s

The mixer’s default max_clients compile-time limit is 16. At 8 clients on a 100 Mbit
LAN uplink, utilisation is < 15% — negligible. On a 1 Gbit switch, 16 clients is
trivial.

shiloh-web-relay adds 3 more relay sessions (one per feed) but these are server-side;
the browser-facing bandwidth is Opus RTP (128 kbps per WebRTC session) rather than raw
S16LE.