Advanced

This section covers topics for developers, integrators, and operators who need to go beyond the default setup.

Sections


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-broadcaster and shiloh-mixer are 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:

  1. Edit → Preferences → Control Surfaces → Open Sound Control → Show Protocol Settings
  2. Enable, set listen port to 3819, reply port to 3820
  3. 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:

  1. Assign a new tag byte in the 0x080x0F range (reserved, currently unused).
  2. Add encode/parse helpers in crates/protocol/src/lib.rs.
  3. If the change is not backward-compatible, increment PROTO_VERSION and update PROTO_MIN if 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 unsafe in the protocol crate (#![forbid(unsafe_code)]).
  • RT-thread code (in shiloh-mixer’s mix.rs and jack_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.