Drone Chorus is a telemetry-driven performance system: Betaflight flight data comes in over MSP, gets smoothed and normalized into a fixed MIDI CC vocabulary, and drives VCV Rack patches, rehearsal workflows, and show surfaces. The repo is built as a field manual and teaching stack for that contract, with the CLI path treated as canonical, the GUI kept honest, and replay/safety/log discipline treated as part of the instrument.
The diagram above keeps the signal chain honest: Betaflight MSP frames roll through msp_bridge.py for smoothing and normalization (the YAML in config/ is the gospel), the CLI launchers (msp_to_midi.py, msp_multi_to_midi.py) light up a virtual/physical MIDI port, and CC14-20 plus CC64 ride into Rack patches or OBS overlays.
The CLI stack is the canonical control surface for full config semantics (signals, runtime, safety, multi-drone orchestration); the GUI is intentionally a single-drone operator panel centered on norm tuning and monitoring.
No breadcrumbs? No problem. This README is still the field manual. The new route pages below make entry easier, but the repo still expects you to care about safety, patching, and receipts.
Mission statement: build performance systems that are safe, legible, and remixable. That means every README doubles as a zine. Expect callouts on safety, assumptions, and hardware quirks right next to the fun bits.
If you want the shortest path into the repo, use these first:
- Choose your route — smallest useful doc set for hearing it, flying it, rehearsing it, teaching it, or contributing.
See:
docs/CHOOSE_YOUR_PATH.md - Current state — what is stable, evolving, experimental, or ready to explore.
See:
docs/CURRENT_STATE.md - Control contract — the canonical telemetry -> normalized signal -> MIDI CC mapping.
See:
docs/CONTROL_CONTRACT.md - Preset gallery — how presets are meant to be named and documented.
See:
docs/PRESET_GALLERY.md
- Control Stack Playbook — soup‑to‑nuts wiring for the pilot rig: MSP→MIDI bridge, CC maps, and per‑drone channels.
See:docs/CONTROL_STACK_PLAYBOOK.md - Safety Checklist — punk‑rock preflight liturgy.
See:docs/checklists/SAFETY.md - Experience Playbook — rehearsal tactics, musical ranges, OBS scene switching.
See:docs/EXPERIENCE_PLAYBOOK.md - Assumption Ledger — what we’re assuming (and how we’ll be wrong).
See:docs/ASSUMPTION_LEDGER.md - UX Map — what the audience sees/hears and how controls surface near the patch edge.
See:docs/UX_MAP.md - GUI Control Room Guide — operating and customizing the PyQt6 dashboard.
See:docs/GUI_CONTROL_ROOM.md - Release Notes Policy — where tagged release notes must live for CI.
See:docs/releases/README.md
Treat that order as gospel when you want the full manual: prototype -> secure -> rehearse -> reflect -> repeat.
- Rapid pilot → ensemble: start with one whoop and one voice; scale to 2–8 drones mapped to channels.
- Open & reproducible: Betaflight + Python + VCV Rack; no black boxes.
- Performance‑ready: OBS scene collection ships with placeholders; relink and go.
- Safety obsessed: airspace, audience, and hearing protection are treated like first-class features.
- Teaching-first: every directory reads like a workshop handout so you can stand up a class or a club night without guessing.
.
├─ config/ # YAML maps (single & multi-drone)
├─ data/ # README + generator for sample MSP logs
├─ docs/ # Playbooks, ledger, UX map, mappings
├─ software/
│ └─ midi-bridge/ # MSP→MIDI (single + multi)
├─ vcv/ # Starter patches (1‑ and 2‑drone)
├─ obs/ # Scene collection (import + relink)
└─ scripts/ # Launchers
| Layer | What you need | Why it matters |
|---|---|---|
| Flight hardware | Betaflight-based quad or whoop with MSP over USB/UART, throttle cap, angle-mode preset. | Stable telemetry keeps the MIDI smoothing honest; the throttle cap prevents accidental prop carnage indoors. |
| Ground station | Laptop with Python 3.10+, audio interface or loopback device, and enough CPU headroom for VCV Rack. | The bridge and Rack patch run side by side—starve either and you’ll hear it. |
| Control surface | Optional MIDI controller or knobs in Rack. | Lets you mix human gestures with telemetry for hybrid performances. |
| Audience rig | OBS (or other broadcaster) plus monitor speakers or a PA with limiters. | Keeps levels under control and lets you document every run. |
If you’re missing actual aircraft, lean on the telemetry captures and the curated sample logs; they were recorded for workshops and regression testing.
- Stop and install Python 3.10+ first. The MSP→MIDI bridge runs on Python, so handle that before trying the quickstart scripts.
- If you're not a coder (or just want a vibe-checked walkthrough), hit the Setup Guide for Non-Coders.
- Python deps — install via
pip install -r software/midi-bridge/requirements.txtto get pinned versions ofmido,pyserial, andPyYAML. - VCV Rack 2 — Community edition works; load the starter patches and add your own modules.
- Virtual MIDI loopback — macOS IAC, Windows loopMIDI, or Linux ALSA
snd-virmidi. The scripts auto-create a virtual port on macOS/Linux; Windows users should add one manually. - OBS 29+ — import the bundled scene collection for ready-to-roll streaming and recording.
- Optional analysis tools —
socatand PySerial’sminitermfor log replay,midimonorMIDI Monitorto visualize CC output. - Optional beginner packaging path —
./scripts/build_gui_binary.shbuilds a desktop app bundle via PyInstaller. - Optional container path —
software/midi-bridge/Dockerfilefor reproducible headless bridge runs.
Keep a python -m venv .venv around if you demo this for others; nothing tanks a workshop like conflicting site packages.
- Betaflight: Angle mode, throttle cap, failsafe; MSP enabled on your USB/UART.
- Deps:
pip install -r software/midi-bridge/requirements.txt- Run the one‑off bridge (perfect for tuning a fresh quad or rehearsing solo):
python3 software/midi-bridge/msp_to_midi.py --serial /dev/ttyUSB0- Swap
/dev/ttyUSB0for your actual rig — skim Find your MSP port if you need a refresher on sniffing the right device. - Default MIDI port: a virtual DroneChorus device. Override with
--midi-port MyHardware --no-virtualif you want to hit a physical DIN box. - Reuse the shared scaling map from
config/multi.yaml; drop a YAML of tweaks via--norm-overrides path/to/my_overrides.yaml. - Optional safety hooks:
--throttle-limit 1500 --estop-file /tmp/drone_chorus.estop.
- VCV Rack: load
vcv/DroneChorus_Patch.vcv, set the Core MIDI‑CC device to DroneChorus, Channel 1. - Fine tune: ride attenuverters; if you need deeper changes, clone
config/multi.yamland point--norm-configat your remix.
Before changing mappings, skim docs/CONTROL_CONTRACT.md so you know which parts of the control vocabulary are stable versus configurable.
This is what you launch when you’re spinning up the full chorus—multiple craft, locked CC maps, each on its own channel.
./scripts/launch_multi.sh- Edit
config/multi.yaml(serial path per drone, 1‑based channel, optionalnorm_overrides). - The launcher spawns one thread per entry, all sharing the same smoothing map.
- For higher drone counts, try the process-based prototype:
./scripts/launch_multi_mp.sh --config config/multi.yamlruntimelets you tunepoll_interval/idle_sleepwhen scaling drone count.publish_interval(per drone) controls worker snapshot cadence in multiprocessing mode.safetyadds bridge-level guardrails (throttle_limit,estop_file,gate_threshold).signalslets you remap CCs or add declarative MSP-derived telemetry fields.- In Rack: instantiate one MIDI‑CC per drone and set channels 1..N.
- Load
vcv/DroneChorus_2Drones.vcvas a template and keep scaling consistent.
Prefer to rehearse with the quad unplugged? The canonical, platform-specific
recipes live under
software/midi-bridge/README.md.
Follow them with the bundled obs/telemetry/bench_hover.mspbin capture or the
rehydrated samples from data/ and you’ll exercise the exact same MSP→MIDI
path the quickstart uses.
Need proof that the MSP→MIDI path works without props spinning? First, rebuild
the sample .mspbin captures (they live as base64 inside the repo to avoid
committing binaries):
python scripts/generate_sample_logs.pyThen pair the workshop logs in data/ with the replay helper:
python examples/replay_log.py data/example_log_01.mspbin --verboseThat single command spins up a virtual DroneChorus-Replay MIDI port,
re-emits CC14–20 + CC64 in real time, and optionally dumps the normalized state
to stdout so you can watch altitude, voltage, and throttle move. Patch that
virtual port into VCV Rack (or your DAW) just like the live rig. Want to change
how the telemetry feels? Edit the norm section in config/mapping.yaml
(slews, ranges, curves) and re-run the command; it’s the fastest way to teach
students how scaling math translates into musical gesture.
If you want the proof/teaching path explained as a first-class surface, see docs/REPLAY_AND_RECEIPTS.md.
| CC | Signal | Notes |
|---|---|---|
| 14 | roll | feeds filters / wavetable scans |
| 15 | pitch | bends FM depth |
| 16 | yaw rate | leans into delay feedback |
| 17 | altitude | comes from MSP_ALTITUDE, falls back to a throttle → 0–3 m ramp if the craft ships without a baro |
| 18 | RSSI | keeps reverb honest |
| 19 | VBAT | nudges compression / tone |
| 20 | throttle | classic VCA fuel |
| 64 | arm gate | sustain-style hold for scene swaps |
The whole mapping is documented like a lab notebook: see
docs/CONTROL_CONTRACT.md for the canonical contract and
docs/CONTROL_STACK_PLAYBOOK.md for the long-form wiring rationale, smoothing
ranges, and how to hack on the YAML. The quick headline is that altitude isn't left to
rot—if MSP_ALTITUDE packets arrive we publish meters directly; otherwise we
lean on throttle so CC17 still animates your patch.
- Physical safety — Follow the Safety Checklist. Indoors? Keep prop guards on, set throttle limits, and respect no-fly bubbles for the crew and audience.
- Hearing safety — Gain-stage inside Rack using the patch cards, then set hard limiters in OBS or your interface. No surprise feedback loops.
- RF discipline — Log every pack and channel in
logs/so you can track interference trends and battery health. - Data hygiene — Treat the bridge like a live instrument. Keep cables tidy, label USB ports, and document any ad-hoc tweaks in the pilot log.
Print the checklist and tape it to the flight case; we’re punk but not reckless.
Import obs/DroneChorus_SceneCollection.json, then relink: FPV Capture, VCV Rack (Window), Program Audio. Studio Mode recommended.
- Run through the repo tour above, then hand folks the Experience Playbook for drills.
- Record each rehearsal in
logs/—treat them as lab reports you can annotate later. - Encourage learners to fork the YAML maps, tweak ranges, and PR back their favorite voicings.
- When in doubt, pair a newcomer with the telemetry playback flow so they can experiment without airspace stress.
- Use
docs/CHOOSE_YOUR_PATH.mdanddocs/CURRENT_STATE.mdwhen you need to onboard mixed audiences quickly.
MIT for code, CC‑BY 4.0 for docs.