System Overview
Shiloh Broadcaster is a low-latency audio distribution system built for live broadcast and
monitoring workflows. It routes audio from one or more sender machines through a central
mix bus and delivers it to any number of listener endpoints — local hardware, networked
playback clients, or WebRTC browser sessions.
All components are Rust binaries except the web UI, which is an Elixir/Phoenix LiveView
application.
Component Summary
| Component | Language | Role |
|---|---|---|
shiloh-broadcaster |
Rust | Captures audio from a PipeWire null-sink and sends it to the mixer over UDP |
shiloh-mixer |
Rust | JACK mix bus: receives ingest, mixes channels, fans out to relay clients |
shiloh-relay |
Rust | Lightweight receiver: plays one mixer bus feed to a local ALSA/cpal device |
shiloh-web-relay |
Rust | WebRTC/WHEP egress: subscribes to the mixer and serves Opus streams to browsers |
mixer_web |
Elixir/Phoenix | LiveView web UI: fader control, VU meters, scene management, MIDI panel |
shiloh-midi-sender |
Rust | Forwards USB MIDI events to the mixer’s MIDI listener over UDP |
Signal Flow Diagram
[Laptop / Studio] [Mixer Server (stg-srv001)] [Listeners]
shiloh-broadcaster shiloh-mixer shiloh-relay
PipeWire null-sink ──UDP──► JACK mix bus ──UDP AUDIO──────────► (Pi, workstation)
JACK capture client (48 kHz JACK, RT thread) cpal → ALSA out
REGISTER_TX / AUDIO_TX 3 buses: main / monitor / cue
shiloh-web-relay
shiloh-mixer ──UDP AUDIO──────────► Opus encoder
WHEP HTTP server
mixer_web (Elixir :8889) ──WebRTC──► browser
polls meters.json / sessions.json
UDP control → :19997
[MIDI keyboard PC]
shiloh-midi-sender ──UDP──► shiloh-mixer :19999 (MIDI listener)
The Mixer Is the Hub
shiloh-mixer is the central process. Everything else connects to it:
- shiloh-broadcaster pushes audio in (ingest protocol, port 5005)
- shiloh-relay / shiloh-web-relay pull audio out (relay protocol, same port 5005)
- mixer_web sends control commands in (control plane, port 19997)
- shiloh-midi-sender sends MIDI note-ons in (MIDI listener, port 19999)
All real-time audio work happens on a JACK RT callback thread inside the mixer. The
control plane, relay fan-out, and web sidecar files run on separate threads and
communicate with the RT thread via lock-free SPSC rings and atomic swaps.
Sidecar State Files
The mixer writes JSON sidecar files to ~/mixer-state/ at runtime. mixer_web reads
these to populate the browser UI.
| File | Contents | Rate |
|---|---|---|
meters.json |
Per-channel peak levels (L/R) | ~10 Hz |
sessions.json |
Active UDP + local + broadcaster sessions | ~5 Hz |
gains.json |
Current fader / mute state | Not currently persisted (in-memory only) |
scenes.json |
Saved scene snapshots | On change |
relay-assignments.json |
Client name → feed (main/monitor/cue) | On change |