Advanced
This section covers topics for developers, integrators, and operators who need to go beyond the default setup.
Sections
- Custom JACK Port Wiring — manual vs. autoconnect,
pw-link, loopback tricks - OSC Integration — Ardour OSC control surface, bidirectional feedback
- Contributing — code style, PRs, testing, license
Building from source
Prerequisites
# Rust stable toolchain
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
source "$HOME/.cargo/env"
# JACK development headers (for shiloh-mixer)
sudo apt install libjack-jackd2-dev
# ALSA development headers (for shiloh-relay)
sudo apt install libasound2-dev
Build all binaries
git clone https://git.shilohbv.com/shiloh/broadcaster
cd broadcaster
cargo build --release
Binaries land in target/release/.
Build a specific crate
cargo build --release -p shiloh-mixer
cargo build --release -p shiloh-broadcaster
cargo build --release -p shiloh-relay
cargo build --release -p shiloh-web-relay
cargo build --release -p shiloh-midi-sender
Cross-compilation
The Cross.toml in the repo root configures cross for the targets used in CI.
Install cross
cargo install cross --git https://github.com/cross-rs/cross
cross requires Docker.
Build for aarch64 Linux (Raspberry Pi)
cross build --release -p shiloh-relay --target aarch64-unknown-linux-gnu
Output: target/aarch64-unknown-linux-gnu/release/shiloh-relay
Build for Windows x86_64
cross build --release -p shiloh-relay --target x86_64-pc-windows-gnu
NOTE:
shiloh-broadcasterandshiloh-mixerare not cross-compiled for Windows — they depend on Linux-specific APIs (PipeWire for the broadcaster, JACK for the mixer).
Custom JACK port wiring
By default, shiloh-mixer uses the [[autoconnect]] table in the TOML to wire hardware device ports to channel inputs. The autoconnect watcher reconnects every ~5 s — if a port disappears and reappears (e.g. after a device reconnect), it is rewired automatically.
For manual wiring with jack_connect:
# List all available JACK ports
pw-jack jack_lsp -c
# Connect a hardware capture port to a mixer channel input
pw-jack jack_connect "PCM2902 Audio Codec Analog Stereo:capture_FL" \
"shiloh-mixer:mic_l_in"
Manual connections made with jack_connect survive only for the current JACK session — they are not persisted. Use [[autoconnect]] in the TOML for anything you want to survive a restart.
Port naming
shiloh-mixer creates input ports named {channel_id}_in for mono channels and {channel_id}_in_1, {channel_id}_in_2 for stereo channels. Output ports are named as configured in [buses] (out_1, out_2, monitor_out_1, etc.).
Check the actual registered ports:
pw-jack jack_lsp | grep shiloh-mixer
OSC integration (Ardour)
shiloh-mixer controls Ardour via two interfaces:
Ardour WebSocket surface (ws://localhost:3818/): used for strip fader, strip mute, and transport play/stop/record. The mixer connects to Ardour’s experimental WebSocket server after startup.
Ardour OSC (127.0.0.1:3819): used for transport navigation commands (goto_start, goto_end, prev_marker, next_marker, add_marker) that the WebSocket surface doesn’t expose.
Ardour → mixer OSC feedback (0.0.0.0:3820): Ardour sends fader and mute changes back to this port. The mixer’s MixerWeb.ArdourOsc.Feedback GenServer (in mixer_web) listens here and updates the ardour_gains state in the Phoenix LiveView.
To configure Ardour for bidirectional control:
- Edit → Preferences → Control Surfaces → Open Sound Control → Show Protocol Settings
- Enable, set listen port to
3819, reply port to3820 - Enable feedback on strips and master
The channel ID → Ardour strip number mapping is configured in [ardour.track_map] in the TOML:
[ardour.track_map]
bcast1 = 1
bcast2 = 2
desktop = 4
Strip numbers are 1-based and match the Ardour mixer strip order. Re-run the template generator after reordering tracks.
Multi-mixer federation
There is no built-in multi-mixer federation. For redundancy or capacity, the simplest approach is to run a second mixer on a different port and point some relay clients / broadcaster senders at it.
For hot-standby: run a passive mixer on a standby server with the same TOML config and no relay clients connected. On failover, update relay clients’ --server flag (or DNS) to point at the standby. Automatic failover would require an external health check + DNS update mechanism.
Protocol extension
The protocol version byte in REGISTER/ACCEPT packets allows future extensions. If you add a new packet type:
- Assign a new tag byte in the
0x08–0x0Frange (reserved, currently unused). - Add encode/parse helpers in
crates/protocol/src/lib.rs. - If the change is not backward-compatible, increment
PROTO_VERSIONand updatePROTO_MINif old clients must be rejected.
Both the server (shiloh-mixer) and the clients check version on ACCEPT — a mismatch results in REJECT 0x02.
Contributing
The codebase is a Cargo workspace:
crates/
protocol/ # Wire format constants + encode/parse helpers
shiloh-mixer/ # Central JACK mix bus
shiloh-broadcaster/# PipeWire → JACK → UDP sender
shiloh-relay/ # UDP → cpal receiver
shiloh-web-relay/ # UDP → Opus → WebRTC/WHEP
shiloh-midi-sender/# USB MIDI → UDP forwarder
Code style
cargo fmt(Rust stable edition)cargo clippy -- -D warnings- No
unsafein the protocol crate (#![forbid(unsafe_code)]). - RT-thread code (in
shiloh-mixer’smix.rsandjack_io.rs): no heap allocation, no locks, no blocking I/O.
Running tests
cargo test --workspace
The protocol crate has roundtrip tests for all packet types. Integration tests require a running JACK server.
Submitting changes
Open a pull request against master on the Forgejo instance. CI runs cargo build --release, cargo test, cargo clippy, and cross-compilation for aarch64-unknown-linux-gnu.
License
See LICENSE in the repository root.