Contributing
Repository structure
broadcaster/
├── Cargo.toml # Workspace root
├── Cross.toml # cross-rs target config
├── crates/
│ ├── protocol/ # Wire format constants, encode/parse helpers
│ ├── shiloh-mixer/ # JACK mix bus, relay fan-out, ingest, MIDI, OSC
│ ├── shiloh-broadcaster/ # PipeWire null-sink → JACK → UDP sender
│ ├── shiloh-relay/ # UDP → cpal relay receiver
│ ├── shiloh-web-relay/ # UDP → Opus → WebRTC/WHEP browser egress
│ └── shiloh-midi-sender/ # USB MIDI → UDP forwarder
├── server/
│ ├── systemd/ # Systemd unit files for the mixer server
│ ├── mixer_web/ # Phoenix LiveView mixer UI (Elixir)
│ └── shiloh-mixer.toml # Example config
└── docs/ # Documentation
Building
Prerequisites:
# Rust (stable, via rustup)
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
source "$HOME/.cargo/env"
# Native build deps
sudo apt install libjack-jackd2-dev libasound2-dev
Build all Rust crates:
cargo build --release
Build a specific crate:
cargo build --release -p shiloh-mixer
cargo build --release -p shiloh-broadcaster
Code style
Rust
All Rust code must pass:
cargo fmt --check
cargo clippy -- -D warnings
Run before every commit:
cargo fmt
cargo clippy -- -D warnings
Conventions:
- No
unsafeincrates/protocol/(#![forbid(unsafe_code)]is enforced). - RT-thread code (the JACK callback in
shiloh-mixer/src/jack_io.rsandmix.rs) must contain:- No heap allocation (no
Vec::push,HashMapinserts,Box::new,String::from, etc.) - No mutex locks (use
AtomicBool,AtomicU64, or lock-free ring buffers) - No blocking I/O (no file reads, no
thread::sleep) - No
println!(use thelogcrate withRelaxedordering guards if needed)
- No heap allocation (no
- Non-RT code has no special restrictions beyond standard Rust idioms.
Elixir (mixer_web)
cd server/mixer_web
mix format
mix credo --strict
Running tests
# All crates
cargo test --workspace
# Single crate
cargo test -p shiloh-protocol
cargo test -p shiloh-mixer
Protocol crate tests: roundtrip encode/parse tests for every packet type. These run without any system dependencies.
Mixer integration tests: require a running JACK server. Set up PipeWire-JACK before running:
systemctl --user start pipewire pipewire-pulse wireplumber
cargo test -p shiloh-mixer -- --test-threads=1
Cross-compilation
The CI builds for aarch64-unknown-linux-gnu (Raspberry Pi) and x86_64-pc-windows-gnu (relay client only). Use cross locally:
cargo install cross --git https://github.com/cross-rs/cross
# Relay for Pi
cross build --release -p shiloh-relay --target aarch64-unknown-linux-gnu
# Relay for Windows
cross build --release -p shiloh-relay --target x86_64-pc-windows-gnu
Note: shiloh-mixer and shiloh-broadcaster are not cross-compiled for Windows — they depend on Linux-specific APIs (libjack, PipeWire).
CI
CI runs on every push to master and on all pull requests. The pipeline:
cargo fmt --checkcargo clippy -- -D warningscargo test --workspacecargo build --releaseforx86_64-unknown-linux-gnucross build --release -p shiloh-relay --target aarch64-unknown-linux-gnu- Publish binaries to
http://broadcaster.shilohbv.com/dist/
PRs that fail any step are not merged.
Pull request guidelines
- Open PRs against
masteron the Forgejo instance atgit.shilohbv.com/shiloh/broadcaster. - One logical change per PR. Large refactors should be split into preparatory + main change.
- Update
docs/if your change affects user-visible behaviour (ports, config keys, CLI flags). - RT-thread changes require extra care — include a before/after xrun count from a 1-minute test run on real hardware.
- Protocol changes that break existing clients must bump
PROTO_VERSIONand document the migration in the PR description.
Protocol extension
If you need to add a new packet type:
- Assign a tag byte in
crates/protocol/src/lib.rs. Tags0x08–0x0Fare reserved for future use. - Add encode and parse functions, with roundtrip tests.
- If the change is backward-incompatible, increment
PROTO_VERSION. UpdatePROTO_MINonly if you want to hard-reject old clients (rather than negotiating). - Add dispatch in
shiloh-mixer/src/broadcast.rsand the relevant client crate.
License
See LICENSE in the repository root.