Metadata-Version: 2.4
Name: conductor
Version: 0.1.72
Summary: AI-powered reactive lighting for DJs and DMX
Author-email: Kris Komar <kriskomar@gmail.com>
Maintainer-email: "Komar Labs, LLC" <help@conductor.lighting>
License: Proprietary
Requires-Python: >=3.10
Description-Content-Type: text/markdown
Requires-Dist: sounddevice>=0.5.0
Requires-Dist: numpy>=1.24.0
Requires-Dist: PyYAML>=6.0
Requires-Dist: ruamel.yaml>=0.18
Requires-Dist: flask>=3.0
Requires-Dist: flask-socketio>=5.3
Requires-Dist: anthropic>=0.40.0
Requires-Dist: python-rtmidi>=1.5.0
Requires-Dist: bleak>=0.21.0
Requires-Dist: eventlet>=0.36.0
Requires-Dist: requests>=2.31.0
Requires-Dist: pywebview>=5.0
Requires-Dist: posthog>=3.0
Requires-Dist: PyAudioWPatch>=0.2.12; sys_platform == "win32"
Provides-Extra: dev
Requires-Dist: pyinstaller>=6.0; extra == "dev"
Requires-Dist: pytest>=8.0; extra == "dev"
Requires-Dist: ruff>=0.5.0; extra == "dev"

# Conductør

**AI-powered reactive lighting for DJs and DMX**

```
 CCC   OO   N   N  DDD   U  U   CCC  TTTTT   ØØ   RRR
C     O  O  NN  N  D  D  U  U  C       T    Ø ØØ  R  R
C     O  O  N N N  D  D  U  U  C       T    Ø Ø Ø  RRR
C     O  O  N  NN  D  D  U  U  C       T    ØØ Ø  R R
 CCC   OO   N   N  DDD    UU    CCC    T     ØØ   R  R
```

Conductør is a real-time audio-reactive lighting controller that listens to music, analyzes it 30 times per second, and drives DMX stage fixtures and smart lights in response. An optional AI director (powered by Claude — bring your own Anthropic key or use the hosted Conductør relay) adjusts the show's artistic parameters every 15 seconds to keep things evolving.

---

## Features

- **Real-time audio analysis** via sounddevice + numpy FFT (cross-platform: Windows WASAPI, Linux PulseAudio/JACK, macOS CoreAudio)
- **DMX control** via Art-Net and sACN/E1.31 with multi-universe support
- **YAML fixture profiles** for any DMX light; define channel maps once, reuse everywhere
- **Role-based reactive mapping**: assign fixtures to roles (bass_mover, mid_wash, high_accent, ambient) and the engine handles the rest
- **AI lighting director**: Claude (bring your own Anthropic API key); adjusts color palette, strobes, movement, decay in real time
- **Show presets**: EDM Club, Chill Lounge, Rock Concert (or create your own with custom AI personalities)
- **Native dashboard**: pywebview-based desktop window with live meters, director controls, preset switching, and manual overrides (also runnable headless for SSH/CI)
- **Govee smart light support** (optional): LAN API control for Govee floor lamps, wall panels, LED strips
- **Bidirectional MIDI**: receive pad taps for blackout/flash/scene changes; send LED feedback to controller pads
- **Beat detection** with per-band tracking, superbeat (all bands spike = white flash), lull detection, and buildup/convergence
- **Animated rainbow logo** on the dashboard

---

## Quick Start

### From Source (Development)

```bash
# Clone the repo
git clone https://github.com/kriskomar/conductor.git
cd conductor

# Create a virtual environment (recommended)
python3 -m venv venv
source venv/bin/activate   # Linux/macOS
# venv\Scripts\activate    # Windows

# Install Conductør and all dependencies
pip install -e .

# Launch — opens a native dashboard window (pywebview)
conductor

# Headless (no window, prints periodic status to the terminal)
conductor --no-window
```

The dashboard opens in its own window. URL is logged at startup if you want to bookmark or hit it from another device on localhost (port is dynamic — see `~/.conductor/server.port`).

On first run, Conductør copies a default configuration to `~/.conductor/`. Edit `~/.conductor/config.yaml` to match your rig.

### From Binary (Release)

1. Download the latest release from [GitHub Releases](https://github.com/kriskomar/conductor/releases)
2. Run `Conductør.app` (macOS) or `Conductor.exe` (Windows) — it opens a native window
3. Edit `~/.conductor/config.yaml` to match your rig
4. Run again

---

## Dependencies

### Python Packages

All installed automatically with `pip install -e .`:

| Package | Version | Purpose |
|---------|---------|---------|
| `sounddevice` | >= 0.5.0 | Cross-platform audio capture (WASAPI/PulseAudio/JACK/CoreAudio) |
| `numpy` | >= 1.24.0 | FFT audio analysis and signal processing |
| `PyYAML` | >= 6.0 | Configuration and fixture profile parsing |
| `flask` | >= 3.0 | Web dashboard HTTP server |
| `flask-socketio` | >= 5.3 | WebSocket real-time state push to browser |
| `anthropic` | >= 0.40.0 | Claude AI director backend |
| `python-rtmidi` | >= 1.5.0 | MIDI input/output (requires system MIDI libraries) |
| `bleak` | >= 0.21.0 | Bluetooth LE for Govee device wake |
| `eventlet` | >= 0.36.0 | Async support for Flask-SocketIO |

### System Dependencies

**Linux (Ubuntu/Kubuntu):**
```bash
sudo apt install libportaudio2 libasound2-dev
```

**Windows:** No system dependencies; PortAudio is bundled with the sounddevice wheel.

**macOS:**
```bash
brew install portaudio
```

### Development Dependencies

```bash
pip install -e ".[dev]"
```

Adds: `pyinstaller` (binary packaging), `pytest` (testing), `ruff` (linting).

---

## Configuration

Conductør is configured via `~/.conductor/config.yaml`. On first run, a default config is copied from the bundled template. The config is also editable via the web dashboard at runtime. Use `--reset-config` to restore defaults.

### config.yaml Reference

```yaml
# Branding
conductor:
  brand: "Conductør"
  tagline: "AI-powered reactive lighting for DJs and DMX"

# Audio capture
audio:
  backend: auto          # auto | wasapi | pulseaudio | jack | device:<name>
  sample_rate: 44100
  fft_size: 2048         # FFT window size (512/1024/2048/4096/8192)
  framerate: 30          # Analysis frames per second (10-120)

# DMX output
dmx:
  protocol: artnet       # artnet | sacn | both
  nodes:
    - ip: 192.168.10.254 # Art-Net node IP
      universe: 0        # Universe number (0-based)

# Fixture rig
fixtures:
  - name: "My Gobo"           # Display name
    profile: holdlamp-120w    # Profile filename (without .yaml)
    address: 1                # DMX start address (1-indexed)
    universe: 0               # DMX universe
    role: bass_mover          # Reactive role

# Govee smart lights (optional)
govee:
  enabled: true
  devices:
    - sku: H6076
      name: "Floor Lamp"
      role: bass_mover
      ble_id: "CA:5F:C3:C6:47:20"  # Optional BLE ID for WiFi wake

# AI Director
director:
  enabled: true
  backend: claude        # claude (direct API) | relay (hosted, Pro/Venue licenses)
  model: claude-haiku-4-5-20251001
  api_key_env: ANTHROPIC_API_KEY   # Env var name containing the API key
  interval: 15           # Seconds between director calls

# MIDI
midi:
  enabled: false
  input_port: auto       # auto or partial port name match
  output_port: auto

# Web dashboard
web:
  enabled: true
  port: 8080
  host: 0.0.0.0          # 0.0.0.0 = accessible from other devices

# Active show preset
active_show: edm-club    # Filename stem from profiles/shows/
```

### Fixture Roles

| Role | Primary Band | Behavior |
|------|-------------|----------|
| `bass_mover` | Bass | Aggressive response, movement on bass beats, gobo cycling, prism activation |
| `mid_wash` | Mids | Moderate response, color cycling, beam effects |
| `high_accent` | Highs | Transient-focused, fast reactions, sharp strobe |
| `ambient` | None | Independent slow color cycle, square-root dimmer curve, no strobe |

---

## Fixture Profiles

Fixture profiles live in `profiles/fixtures/` as YAML files. Each profile describes a fixture's DMX channel layout so the role engine knows how to drive it.

### Creating a Profile

1. Copy `profiles/fixtures/_template.yaml`
2. Rename to match your fixture (e.g., `chauvet-intimidator-160.yaml`)
3. Fill in the channel map from your fixture's DMX chart

### Channel Types

| Type | Description |
|------|-------------|
| `pan`, `pan_fine` | Pan position and fine adjustment |
| `tilt`, `tilt_fine` | Tilt position and fine adjustment |
| `dimmer` | Master brightness |
| `strobe` | Strobe speed (0 = off) |
| `speed` | Motor/movement speed |
| `color_wheel` | Discrete color wheel positions |
| `gobo_wheel` | Gobo/pattern wheel positions |
| `prism` | Prism on/off/rotation |
| `rgb_r`, `rgb_g`, `rgb_b`, `rgb_w` | RGB(W) color channels |
| `led_select` | LED group/color selection |
| `beam` | Beam control |
| `flash` | Flash trigger |
| `rotation` | Rotation speed/direction |
| `effect`, `effect_speed` | Effect selection and speed |
| `function` | Generic (set default value) |
| `reset` | Reset trigger |

### Example Profile

```yaml
name: "My Par Light"
manufacturer: "Generic"
channels: 7
category: par
channel_map:
  1: { type: dimmer, range: [0, 255] }
  2: { type: rgb_r, range: [0, 255] }
  3: { type: rgb_g, range: [0, 255] }
  4: { type: rgb_b, range: [0, 255] }
  5: { type: strobe, range: [0, 255] }
  6: { type: function, default: 0 }
  7: { type: function, default: 0 }
```

---

## Show Presets

Show presets live in `profiles/shows/` and bundle color palettes, energy thresholds, and AI director personalities.

### Included Presets

| Preset | Description |
|--------|-------------|
| `edm-club` | High-energy electronic music; aggressive strobes, fast movement |
| `chill-lounge` | Relaxed ambient; no strobes, slow movement, warm palette |
| `rock-concert` | Raw and punchy; bold reds, strong contrast, dark silences |

### Creating a Preset

```yaml
name: "My Custom Show"
description: "What this show is about"
color_palette:
  bass: { hue_range: [0.0, 0.08], saturation: 0.95 }
  mids: { hue_range: [0.25, 0.50], saturation: 0.9 }
  highs: { hue_range: [0.60, 0.85], saturation: 0.85 }
energy:
  beat_threshold: 0.55
  superbeat_threshold: 0.60
  strobe_multiplier: 1.5
  movement_multiplier: 1.8
  release_speed: 0.04
director:
  personality: "Your artistic directive for the AI goes here."
  interval: 15
```

---

## AI Director

The AI director runs in a background thread and calls your chosen LLM every N seconds with a snapshot of the show's state (average energy per band, beat counts, lull percentage, buildup factor). It returns adjusted parameters that shape the show's feel.

### Supported Backends

| Backend | Config Value | API Key Env Var | Cost |
|---------|-------------|----------------|------|
| Claude (Anthropic, direct) | `claude` | `ANTHROPIC_API_KEY` | ~$0.01/hour with Haiku |
| Claude via Conductør relay | `relay` | None (uses license key) | Included with Pro/Venue tiers up to daily cap |

### What the Director Controls

- **Hue offsets** per band (shift color palette)
- **Strobe multiplier** (0 = disabled, 2 = aggressive)
- **Movement multiplier** (fixture pan/tilt speed)
- **Par wash speed** (ambient color cycle rate)
- **Superbeat threshold** (sensitivity for all-band white flash)
- **Release speed** (how fast lights decay between beats)
- **Mood** (displayed as big ASCII text: FIRE, DREAMY, CHAOS, etc.)
- **Note** (brief artistic reasoning)

The director is optional. If no API key is configured, Conductør runs with default parameters and the reactive engine handles everything.

---

## MIDI

### Input Mapping

MIDI note-on and CC messages trigger show actions. Default mapping (customizable in `profiles/midi/`):

| MIDI Note | Action |
|-----------|--------|
| 36 (Pad 1) | Blackout |
| 37 (Pad 2) | Full room white flash |
| 38 (Pad 3) | Cycle to next show preset |
| 39 (Pad 4) | Strobe override |

| CC | Parameter |
|----|-----------|
| 1 (Knob 1) | Strobe multiplier (0.0 - 2.0) |
| 2 (Knob 2) | Movement multiplier (0.3 - 2.5) |

### Output Feedback

Controllers with LED pads receive note-on messages reflecting:
- Beat energy pulses on band pads
- Active scene highlight on scene pads

---

## Web Dashboard

The web dashboard runs on `http://localhost:8080` by default and is accessible from any device on the network.

### Features

- **Live band meters** (bass/mids/highs)
- **AI director mood and note** display
- **Quick actions**: Blackout, Flash, Next Scene
- **Director control sliders**: strobe, movement, release, superbeat threshold
- **Show preset selector**
- **Event log** (scrolling, color-coded)
- **Show stats**: lull %, buildup, convergence, uptime

### API Endpoints

| Method | Endpoint | Description |
|--------|----------|-------------|
| GET | `/api/state` | Current show state snapshot |
| GET | `/api/config` | Full configuration |
| GET | `/api/fixtures` | Configured fixtures |
| GET | `/api/director` | Director state and params |
| POST | `/api/director/override` | Push parameter overrides |
| GET | `/api/presets` | List show presets |
| POST | `/api/presets/activate` | Activate a preset |
| POST | `/api/action/blackout` | Trigger blackout |
| POST | `/api/action/flash` | Trigger white flash |
| GET | `/api/audio/devices` | List audio inputs |
| GET | `/api/midi/ports` | List MIDI ports |
| GET | `/api/log` | Recent event log |
| GET | `/help` | This documentation |

---

## CLI Flags

```
conductor [OPTIONS]

Options:
  --config, -c PATH    Path to config.yaml (default: ~/.conductor/config.yaml)
  --reset-config       Reset config to defaults (overwrites ~/.conductor/)
  --no-window          Don't open the desktop window (headless / SSH mode)
  --no-terminal        Disable the periodic status log printed in headless mode
  --no-web             Disable the web dashboard server entirely
  --no-govee           Disable Govee smart lights
  --no-director        Disable AI director
  --no-dmx             Disable DMX output
  --no-midi            Disable MIDI
  --smoke              Smoke-test mode (implies --no-window --no-terminal, auto-exits after 30s)
```

---

## Environment Variables (Internal / QA)

These aren't customer-facing; they're for development and QA against test-mode
LemonSqueezy keys. Don't document them on the website.

| Variable | Purpose |
|---|---|
| `CONDUCTOR_DEV_SECRET` | Dev bypass — short-circuits licensing entirely and grants Venue tier without any LS call. |
| `CONDUCTOR_LICENSE_KEY` | Override the cached license at startup; useful for swapping between test keys without re-running activation flow. |
| `CONDUCTOR_ALLOW_TEST_KEYS` | Permit activation/validation of LS **test-mode** keys. Default is reject. Set to `1` on QA/tester boxes when running a real release build against a sandbox key. |
| `CONDUCTOR_IDLE_THRESHOLD_SECONDS` | Override the user-configured idle auto-pause threshold (Settings → Director). Set to e.g. `5` to fire the pause after 5 seconds of no dashboard activity. Used for QA — disables the dropdown UI to make the override visible. Default unset = use the saved setting. |

The relay (Lambda) has the matching `ALLOW_TEST_KEYS=1` env var on the deploy
side. Both must be set for a test key to flow through end-to-end (app activates,
relay accepts AI Director calls).

### When something's wrong (KL-107)

Two escape hatches for when the running app or local install gets weird:

| Command | What it nukes |
|---|---|
| `conductor --reset-app-state` | Kills any running conductor processes, clears WKWebView cache (the pywebview window's `~/Library/WebKit/...` data store), removes stale `~/.conductor/server.{pid,port}` lockfiles. Doesn't touch your user data (fixtures, midi mappings, license). Exits without launching — re-run conductor normally afterward. |
| `conductor --reset-app-state --reset-config` | Above PLUS wipes `~/.conductor/` entirely. Fresh-install state. Use sparingly. |
| `bin/rebuild-venv.sh` | Nukes `.venv/` and rebuilds from scratch. Use when pip/Python state is corrupted (mystery import errors, dependency drift, half-installed packages). Doesn't touch user state. |

If everything's broken, the order is: `--reset-app-state --reset-config` first, then
`bin/rebuild-venv.sh` if Python itself is suspect.

---

## Versioning & Releases

Conductør uses **git tag-based versioning**. To create a release:

```bash
# Bump version in pyproject.toml and conductor/__init__.py
# Then:
git tag v0.2.0
git push --tags
```

The GitHub Actions workflow automatically builds Windows and Linux binaries and publishes a GitHub Release.

---

## Architecture

```
conductor/
├── audio/         — Cross-platform audio capture + FFT analysis
├── reactive/      — Beat detection, lull, superbeat, buildup engine
├── dmx/           — Art-Net + sACN senders, multi-universe state
├── fixtures/      — YAML profiles, role engine, frame builder
├── director/      — AI director (Claude direct or via relay), presets
├── govee/         — Smart light discovery + control
├── midi/          — Bidirectional MIDI input + LED feedback
├── web/           — Flask-SocketIO dashboard + REST API
├── dashboard/     — Terminal ANSI dashboard
├── logo.py        — Animated ASCII logo renderer
├── state.py       — Thread-safe shared show state
├── config.py      — YAML config loader + validation
├── bootstrap.py   — First-run config setup (~/.conductor/)
└── main.py        — Entry point, orchestration, shutdown

config-template/   — Default config copied to ~/.conductor/ on first run
├── config.yaml    — Main configuration
├── fixtures/      — Fixture hardware profiles
├── shows/         — Show presets
└── midi/          — MIDI controller mappings
```

---

## Community

Conductør is being built in the open. The Discord is where the conversation
happens — feature ideas, fixture profile sharing, gig videos, and direct line
to the founder.

**Join:** https://discord.gg/xUYgdCz8Rh

---

## License

MIT

---

Built with Python, numpy, Flask, and a lot of loud music.
