Metadata-Version: 2.1
Name: polyclav
Version: 0.2.0
Summary: Live-piano host: MIDI keyboard -> soundfont/plugin synthesis -> system audio (Linux PipeWire / macOS CoreAudio)
Author: Matthew Schulkind
License: Apache-2.0
Project-URL: Homepage, https://github.com/mschulkind-oss/polyclav
Description-Content-Type: text/markdown
Classifier: Operating System :: POSIX :: Linux
Classifier: Operating System :: MacOS
Classifier: License :: OSI Approved :: Apache Software License
Classifier: Topic :: Multimedia :: Sound/Audio :: MIDI
Requires-Python: >=3.8

# polyclav

A self-contained **live piano host** for Linux and macOS. Plug in a MIDI
keyboard and an audio interface, run `polyclav`, pick your keyboard, and you
have a digital piano: keys make piano sound, effects are in the chain, and
(optionally) a Novation Launchkey's knobs, pads, and screen drive the front
panel. No DAW, no recording — just playing. Devices can come and go;
polyclav reconnects automatically and idles at near-zero CPU when nothing
is plugged in.

> Picking your keyboard is a one-time step: `[midi].allow_devices` is an
> allowlist, so nothing sends notes until you name it. Startup prints your
> connected port names and the exact line to add — or tick a box in the web
> UI. See `docs/USER_GUIDE.md`.

> **Status:** Linux (PipeWire) is the primary platform — developed and
> hardware-tested against a Novation Launchkey 61 MK4 + Behringer XR18
> over OSC. macOS (Apple Silicon) is supported: releases ship a macOS
> wheel and a Homebrew formula, though plugin hosting (LV2, CLAP) is
> Linux-only in this build. Should work with any class-compliant MIDI
> keyboard and any supported audio interface; Launchkey-specific bits
> gracefully degrade if the device isn't present.

polyclav is implemented in Go with a thin Rust `audio-core` for the
real-time audio thread (PipeWire, oxisynth, sfizz). A `polyclav-components`
CLI is also included for encoding/uploading Launchkey MK4 Custom modes
over SysEx.

---

## What works today

- **Live audio out** via PipeWire. Any default sink works; an XR18 with
  a low-latency WirePlumber rule (see `docs/INSTALL.md`) gets ~8 ms
  round-trip at a 128-frame quantum.
- **Live MIDI in** over ALSA-seq (rtmidi). Notes, CCs, mod wheel, and
  pitch bend forwarded to the synth end-to-end.
- **Four synth backends**, picked per patch:
  - `.sf2` / `.sf3` → oxisynth (pure Rust)
  - `.sfz`         → sfizz (C++ via thin Rust FFI)
  - `type = "native"` → built-in pure-Rust analog-style synth
  - `type = "lv2"` / `type = "clap"` → plugin hosting
- **Patches** — named presets defined in `[[patches]]` in the
  config. Top-row Launchkey pads select patches live (8 visible).
- **Per-patch gain matching** via `gain_db` so switching a soft EP for
  a loud grand doesn't blow your ears off.
- **Soundfont hot-swap** — switch patches without dropping audio.
- **DSP chain** in the audio thread, in order:
  `synth → drive_pedal → chorus → tremolo → analog_delay → patch_gain → input_comp → reverb → mastering_comp → limiter → master_volume → out`.
  Knobs 1/2/3/4 drive master volume, reverb, the input compressor, and a
  Tube-Screamer-style drive pedal live — all four work on every patch
  type, soundfont or native. The chorus, tremolo, and analog-delay
  pedals are implemented and wired into the chain (default off,
  bit-exact bypass) but not yet reachable from a Launchkey knob, REST
  field, or per-patch persistence — see `docs/VISION.md` §1b–1d. See
  also `docs/OPEN_SOUND_ENGINES.md`.
- **Launchkey knob pages** — five pages (MAIN / OSC / FILTER / AMP /
  LFO/MOD) on Scene ▲/▼ put the whole native-synth voice on the 8
  encoders, with page indicators on the bottom pad row. Code-complete,
  pending hardware verification (`docs/HARDWARE_TESTS.md`).
- **Web dashboard** — an embedded, localhost-only web UI (`[web]` in the
  config, on by default, `127.0.0.1:8666`): a Next.js app (static
  export embedded in the binary — `go build` needs no Node) with patch
  switching, all live params, mastering, the full native-synth panel, a
  velocity-curve editor with a live note monitor, validated in-browser
  config editing, a generic MIDI device probe/reverse-engineering tool,
  and the audition transport, plus a REST + SSE API.
  The pre-Next single-file page remains at `/legacy`. See
  `docs/WEB_UI.md` and `docs/MIDI_PROBE.md` (the device probe).
- **Velocity curves** — global `[midi.velocity]` curve (soft / linear /
  hard / custom gamma, or 2–16 control points, + output clamp) with
  per-patch overrides, so every patch responds to your keybed the way
  you want — editable live from the browser. See
  `docs/VELOCITY_CURVES.md`.
- **Audition mode** — `polyclav --play <clip> [--loop] [--tempo N]` plays
  one of seven built-in diagnostic clips through the full audio path, no
  keyboard needed; the web dashboard has the same transport. See
  `docs/AUDITION.md`.
- **Native synth — a full Minimoog-style voice**: 3 oscillators
  (saw/square/pulse, octave, detune, level) + noise + pre-filter drive,
  resonant ladder filter with its own ADSR and keyboard tracking, a
  runtime amp ADSR, velocity→amp/cutoff routing, a global LFO
  (pitch/cutoff/amp), mod-wheel vibrato, pitch bend, glide, optional 2×
  oversampling, and up to **8-voice polyphony** with live-switchable
  voice modes. Every tweak persists per patch in `state.toml`. See
  `docs/NATIVE_SYNTH.md`.
- **Optional mixer OSC bindings** — faders and pads can drive mixer faders
  and mute toggles over UDP once explicitly configured. Bindings live in
  `[osc.mixer]` (preferred name; `[osc.xr18]` still works), with a
  configurable presence-check `heartbeat` for non-X-Air OSC targets.
- **Launchkey MK4 DAW driver** — handshake, knob/pad/screen control,
  per-patch knob-value persistence. The `polyclav-components` CLI also
  encodes and uploads Custom modes over SysEx.

For the developer-facing rundown of every component, see `AGENTS.md`.

---

## Install

polyclav ships as prebuilt binaries: a wheel on PyPI (Linux x86_64 +
macOS arm64) and a Homebrew formula (Linux x86_64 + macOS arm64). On
Linux the binary is dynamically linked against your **system's** audio
libraries — install those from your distro first. On macOS the binary
links nothing beyond the system frameworks, so no prerequisites are
needed.

### 1. System libraries (Linux only)

PipeWire, ALSA, and the LV2 host library (lilv):

```sh
# Debian / Ubuntu
sudo apt install pipewire libasound2 liblilv-0-0
# Fedora
sudo dnf install pipewire alsa-lib lilv
# Arch
sudo pacman -S pipewire alsa-lib lilv
```

**Optional — sfizz**, for `.sfz` sample libraries (Salamander Grand, etc.). It's
a runtime-loaded *optional* backend: without it, `.sfz` patches are silent but
SF2/SF3 soundfonts, the native synth, and LV2/CLAP plugins all work. sfizz isn't
always in a distro's default repos — check your package manager (e.g. the AUR on
Arch) or build it from source. Run `polyclav doctor` to see what's available.

### 2. polyclav

```sh
uvx polyclav            # run without installing (Linux + macOS)
pipx install polyclav   # or install it persistently
```

Both fetch a prebuilt wheel from PyPI; the `polyclav-components` Launchkey
SysEx CLI ships in the same wheel. Or, with Homebrew (Linux x86_64,
macOS Apple Silicon):

```sh
brew install mschulkind-oss/tap/polyclav
```

On Linux, Homebrew does not provide the §1 system libraries — you still
install those from your distro, and the binary needs glibc ≥ 2.39
(Debian 12 / Ubuntu 22.04 are too old; build from source there).

There is no `go install` path: the binary links a Rust staticlib that
cargo builds next to the Go code, and the Go module proxy can't provide
it — [build from source](#build-from-source) instead.

### 3. First run

```sh
polyclav doctor         # report backends + analyse your config
polyclav bootstrap      # download free starter soundfonts (~500 MB; prompts for licenses)
polyclav                # start playing
```

---

## Build from source

```sh
mise install                                     # Go + Rust toolchains
just build                                       # Rust audio-core + Go binary
just install                                     # or: install both binaries to ~/.local/bin
mkdir -p ~/.config/polyclav
cp polyclav.example.toml ~/.config/polyclav/config.toml
$EDITOR ~/.config/polyclav/config.toml             # edit soundfont paths
overmind start -D                                # run as daemon via Procfile
```

`polyclav` ships no soundfonts (license + size). The example config points
at `~/.local/share/polyclav/soundfonts/...` — drop your files there or edit
the paths. `docs/INSTALL.md` lists free starter soundfonts and where to
get them.

If you don't have a Launchkey, the daemon still runs: the audio path and
any MIDI keyboard work; the Launchkey-specific code paths just stay idle.

---

## Documentation

| Path | What it covers |
|---|---|
| [`docs/INSTALL.md`](docs/INSTALL.md) | System-level install: build deps, soundfonts, hardware notes. Start here on a fresh machine. |
| [`docs/USER_GUIDE.md`](docs/USER_GUIDE.md) | End-user install / configure / play, including the full config schema. |
| [`docs/HARDWARE_TESTS.md`](docs/HARDWARE_TESTS.md) | Hardware verification checklist (Launchkey MK4 + XR18). |
| [`docs/ROADMAP.md`](docs/ROADMAP.md) | Native-synth design record (Phases 2-4, now implemented) + open questions. |
| [`AGENTS.md`](AGENTS.md) | For AI agents and developers working on the code. Build/test workflow, current state, DSP API. |

---

## License

polyclav itself is licensed under the **Apache License, Version 2.0** —
see [`LICENSE`](LICENSE).

Transitively used libraries carry their own licenses:

- [`oxisynth`](https://github.com/PolyMeilex/OxiSynth) — LGPL-2.1
- [`sfizz`](https://github.com/sfztools/sfizz) — BSD-2-Clause
- [`pipewire-rs`](https://gitlab.freedesktop.org/pipewire/pipewire-rs) — MIT or Apache-2.0
- [`rtmidi`](https://github.com/thestk/rtmidi) (via cgo) — MIT-like (see upstream)

If you distribute polyclav (or a derivative), bundle the appropriate
notices. Apache 2.0 + LGPL-2.1 (oxisynth) means: dynamic linking is
fine, you must permit relinking against modified oxisynth, and you must
preserve oxisynth's copyright notices.

---

## Contributing

Patches welcome. The workflow, code layout, and current milestones are
documented in [`AGENTS.md`](AGENTS.md). Run `just check` before sending
anything — it gates on `cargo build --release`, `cargo clippy -D warnings`,
`go vet`, `cargo test`, `go test`, and `go build ./...`.

Bug reports: please include `polyclav --version`, the contents of your
`config.toml` (redact paths if you like), and the relevant slice of
`/tmp/polyclav.log` (overmind tees there by default).
