A TDD-driven AirPlay 2 receiver that runs on minimal hardware (Raspberry Pi Zero 2 W) while gracefully scaling to more powerful devices. The system detects hardware capabilities at runtime and provides precise audio latency calibration through an iOS companion app.
Built in Rust for maximum performance, minimal memory footprint, and memory safety.
Status: Receiver service, Bonjour discovery, and structured calibration implemented; installer provisions systemd + Avahi and pre-generates calibration signals.
- ✅ Phase 1 specification document
- ✅ Cargo workspace structure
- ✅ Shared protocol types (device, messages, calibration)
- ✅ Hardware detection module (test-first)
- CPU core/RAM detection, board identification (Pi Zero 2 W, Pi 4, Pi 5)
- Audio output detection (I2S, USB, HDMI, Headphone)
- ✅ Shairport-sync config generator
- Dynamic config generation with audio output mapping, soxr interpolation, buffer sizing, cover art
- ✅ Receiver HTTP service (Axum)
- Discovery: Avahi TXT for
_airsync._tcpwith name/ver/api/caps/id - Calibration (structured mode):
/api/calibration/spec,/api/calibration/request,/api/calibration/ready,/api/calibration/result- Pre-generated 48 kHz structured WAV with marker metadata
- Warm-up hum + multi-frequency markers for reliable detection
- Applies latency via shairport config + restart
- Settings endpoints:
/api/settings(GET/POST) update shairport config and restart shairport-sync - Receiver info endpoint and TXT helpers
- Discovery: Avahi TXT for
- ✅ CLI tools:
airsync-detect,airsync-generate-config,airsync-receiver-servicebinary - ✅ Installer provisions
- Builds/installs receiver service and pre-generated calibration WAV
- Installs systemd unit for receiver service and shairport-sync
- Publishes Avahi
_airsync._tcpwith caps/name/id - Creates state dir
/var/lib/airsyncfor receiver_id/cache
- ⏳ Test installer on real Raspberry Pi hardware
- ⏳ Shairport-sync process manager (start/stop/monitor/recover)
- ⏳ Configuration generator with interactive prompts
- ⏳ WebSocket API for iOS companion app
The fastest way to install on a Raspberry Pi with internet:
curl -sSL https://raw.githubusercontent.com/JackDarnell/Airsync-Platform/main/installer/scripts/install.sh | sudo bashThis automatically:
- ✅ Downloads source from GitHub
- ✅ Detects your hardware (Pi 4 1GB+, Pi 5, etc.)
- ✅ Installs all dependencies (Rust, build tools, ALSA, Avahi)
- ✅ Builds and installs shairport-sync
- ✅ Builds and installs AirSync daemon
- ✅ Generates optimized configuration
- ✅ Sets up systemd service (auto-start on boot)
Takes ~5-10 minutes. After installation, your device appears in iOS Control Center!
For offline installation or local testing:
# On your Mac/Linux machine:
./installer/scripts/package-for-pi.sh
# Copy to your Raspberry Pi:
scp airsync-offline-installer.tar.gz pi@raspberrypi.local:~/
# On the Raspberry Pi:
tar -xzf airsync-offline-installer.tar.gz
cd airsync
sudo SOURCE_DIR=$PWD ./install.shSee installer/README.md for troubleshooting and manual installation.
- Rust 1.75+ (install via rustup)
- Docker Desktop (for testing in Pi environment)
- Git
- (Optional) Cross-compilation tools for ARM
# Clone repository
git clone https://github.com/JackDarnell/airsync.git
cd airsync
# Build all crates
cargo build
# Build optimized release binary
cargo build --releaseThe fastest way to verify your code works in a Linux environment:
# One command to test everything in a simulated Pi environment
./docker/test-in-docker.shOr manually:
# Enter Docker Pi simulator
cd docker/pi-simulator && docker-compose up -d
docker-compose exec pi-zero-2w bash
# Inside container:
cargo test
cargo run --bin detect-hardware
# Exit
exit
docker-compose downThis simulates a Raspberry Pi Zero 2 W with mocked /proc files.
To test the full installation process in Docker:
cd docker/pi-simulator
./test-installer.shThis runs the complete installer in a fresh Debian container.
This project follows Google's TDD philosophy. All features are tested before implementation.
# Run all tests
cargo test
# Run tests for a specific crate
cargo test -p airsync-receiver-core
# Run tests in watch mode (requires cargo-watch)
cargo install cargo-watch
cargo watch -x test
# Run tests with output
cargo test -- --nocapture
# Run tests with coverage (requires cargo-tarpaulin)
cargo install cargo-tarpaulin
cargo tarpaulin --out Html
# Run iOS calibration unit tests (requires Xcode + simulator)
cd mobile-applications/AirSync
xcodebuild test -scheme AirSync -destination 'platform=iOS Simulator,name=iPhone 17'Tests follow the 80/15/5 pyramid:
- Unit tests (80%): Embedded in source files with
#[cfg(test)]modules - Integration tests (15%): In
tests/directory (coming soon) - E2E tests (5%): Full system tests with Docker Pi emulator (coming soon)
✓ Receiver Core (38 tests) — hardware detection, shairport config, calibration applier, structured calibration spec + playback, Avahi/TXT helpers, settings API
✓ Shared Protocol — serialization for calibration markers/spec
✓ iOS unit build — calibration models/detector/spec decoding (sim build; deprecation warnings only)
airsync/
├── crates/
│ ├── receiver-core/ # Main receiver daemon (Rust)
│ └── shared-protocol/ # Cross-platform types (Rust)
├── installer/ # One-command installation ✅ (installs receiver + shairport + Avahi)
│ ├── scripts/install.sh # Main installer script
│ └── README.md # Installation guide
├── docker/ # Docker Pi simulator ✅
│ └── pi-simulator/ # Test environment
├── docs/ # Architecture documentation ✅
│ └── shairport-sync-integration.md
├── mobile-applications/AirSync/ # Swift iOS companion app (calibration + pairing)
├── tools/
│ ├── hw-profiler/ # Hardware benchmarking (coming soon)
│ └── latency-analyzer/ # Calibration data analysis (coming soon)
└── Cargo.toml # Workspace configuration
- airsync-shared-protocol: Type definitions for hardware capabilities, WebSocket messages, and calibration protocol
- airsync-receiver-core: Receiver HTTP service, hardware detection, shairport config, calibration engine, pairing/settings API
- Binaries:
detect-hardware,generate-config,airsync-receiver-service - Modules:
hardware(detection),airplay(config),http(pairing/calibration/settings)
- Binaries:
- Red: Write a failing test that defines desired behavior
- Green: Write minimal code to make the test pass
- Refactor: Clean up code while keeping tests green
- Repeat: Move to next smallest unit of work
- Self-describing code: Use descriptive function names that represent the domain
- Avoid comments: Code should explain itself through clear naming
- High cohesion: Related functionality stays together
- Low coupling: Minimize dependencies between modules
- Shared test data: Use centralized test fixtures that are easy to modify
# Run tests in watch mode during development
cargo watch -x test
# Check code with clippy (Rust linter)
cargo clippy
# Format code
cargo fmt
# Build for development (fast compilation, debug symbols)
cargo build
# Build for release (optimized, small binary)
cargo build --release
# Run hardware detection tool (requires Raspberry Pi or Linux with /proc)
cargo run --bin detect-hardware# Add ARM target
rustup target add aarch64-unknown-linux-gnu
# Install cross-compilation toolchain (macOS example)
brew install aarch64-unknown-linux-gnu
# Build for Pi Zero 2 W / Pi 4 / Pi 5 (ARM64)
cargo build --release --target aarch64-unknown-linux-gnu
# Binary will be at: target/aarch64-unknown-linux-gnu/release/- Memory Safety: No segfaults, no data races, guaranteed at compile time
- Zero-Cost Abstractions: Performance of C with high-level expressiveness
- Tiny Binaries: Release builds optimized for size (typically <2MB stripped)
- Low Memory Footprint: Perfect for Pi Zero 2 W's 512MB RAM
- Excellent Testing: Built-in test framework with mocking support
- Cross-Compilation: Easy to build ARM binaries from any platform
Runtime detection of device capabilities drives feature availability:
- CPU cores: Parsed from
/proc/cpuinfo - RAM: Parsed from
/proc/meminfo - Board ID: Identifies Pi Zero 2 W, Pi 4, Pi 5 from model string
- Audio outputs: Detects I2S DAC, USB audio, HDMI, headphone jack via ALSA
- Hardware profiles: Selects minimal/standard/enhanced based on resources
Implementation: crates/receiver-core/src/hardware/detector.rs
Wraps shairport-sync with:
- ✅ Dynamic configuration based on hardware profile
- Profile-aware interpolation (basic/soxr)
- RAM-aware buffer sizing
- Audio output device detection
- ⏳ Process management (start/stop/monitor)
- ⏳ Health monitoring and auto-recovery
- ⏳ mDNS advertisement with device metadata
Architecture: We don't bundle shairport-sync. It's installed as a system dependency, and we generate configs + manage the process. See docs/shairport-sync-integration.md
Structured calibration flow between receiver and iOS:
- Receiver generates a structured 48 kHz WAV at install/startup with warm-up hum, multi-frequency markers, and trailing click.
GET /api/calibration/specreturns marker metadata (sample rate, length, markers) for iOS.POST /api/calibration/request+POST /api/calibration/readyschedule playback using server time; playback uses the pre-generated WAV (or chirp fallback).- iOS records with padded window, runs matched filtering over the markers (sub-sample interpolation), computes latency/confidence, and can apply the offset via
POST /api/calibration/result.
See PHASE_1_SPECIFICATION.md for detailed technical specifications.
- Test first, always: No code without tests
- Smallest valuable increment: Break work into tiny, testable units
- Clean as you go: Refactor immediately, don't accumulate debt
- Domain-driven naming: Names should reflect business concepts
Tests run automatically on every push:
- Unit and integration tests on Ubuntu latest
- E2E tests in QEMU ARM64 emulator
- 80%+ code coverage required on critical paths
[License information to be added]
For issues and questions, please use GitHub Issues.