Skip to content

Make audx marketable: zero-config synth kit, PortAudio-optional install, 4x tests, full docs - #4

Merged
chrisschouk merged 9 commits into
mainfrom
claude/audx-team-launch-wyajz5
Jun 21, 2026
Merged

Make audx marketable: zero-config synth kit, PortAudio-optional install, 4x tests, full docs#4
chrisschouk merged 9 commits into
mainfrom
claude/audx-team-launch-wyajz5

Conversation

@chrisschouk

Copy link
Copy Markdown
Owner

Why

audx was a well-architected terminal DAW that couldn't make a sound out of the box (every render needed your own sample files) and hard-crashed on import on any machine without the PortAudio C library — so pip install audx → first run failed. This PR fixes both and rounds the project into a state you could put in front of people.

The headline: it makes music in 10 seconds, zero config

pip install audx
audx demo loop.wav        # full multi-track beat, no samples, no audio device

1. Built-in synth kit (audx/synth.py)

14 pure-numpy procedurally-synthesised drum/perc voices — kick, sub/808, snare, clap, snap, hh, oh, rim, tom, cowbell, perc, ride, crash, shaker — with aliases, per-voice tune, velocity scaling and deterministic (seeded) output. The renderer falls back to the synth kit whenever a sample of that name isn't found, so:

audx render "kick 4/4"               # synthesises — no --sample needed
audx render "cowbell e(5,16,2)"
audx render "sub e(3,8) | tune -5st"

Your own audio always wins when present; --sample is now optional. New audx demo and audx synths commands.

2. Install actually works on a clean machine

sounddevice is now imported lazily, so the CLI and all offline features (render, export, demo, diff, project ops) need no system libraries. Only live real-time playback requires PortAudio, with a friendly install hint when missing. Verified: importing the whole CLI no longer loads sounddevice.

3. Quality

  • Tests 30 → 123 (pytest), coverage ~28% → ~43%; CI gate raised to 40%. synth.py at 96%.
  • mypy now type-checks the entire package and passes clean (was 4 files). Fixed real type issues in midi, web, calibration, voice, groove; added types-requests.
  • Fixed a genuine UX bug: audx mix set <ch> gain -3 (documented negative dB) was rejected as an unknown option.

4. Marketable docs

Rewritten README (10-second demo hero, DSL table, grouped command overview, honest "what it isn't yet"), LICENSE (MIT), CHANGELOG.md, CONTRIBUTING.md, docs/getting-started.md, docs/synth-kit.md, and a static site/ landing page (dark terminal aesthetic, no build step).

Verification

ruff check src tests   # clean
mypy src/audx          # clean, 38 files
pytest -q              # 123 passed
audx demo loop.wav     # 7.7s stereo WAV, full RMS, non-silent

Notes

  • I kept all changes truthful to current behaviour — unfinished features (plugin hosting, voice control, etc.) still print honest "not wired up yet" messages rather than pretending.
  • warn_return_any is intentionally disabled in mypy config (numpy stubs return Any); every other strict flag stays on. Rationale documented inline in pyproject.toml.

🤖 Generated with Claude Code


Generated by Claude Code

claude added 9 commits June 21, 2026 04:49
…l offline use

- Add audx/synth.py: pure-numpy procedural drum/perc voices (kick, snare,
  hh, oh, clap, rim, tom, cowbell, perc, ride, crash, shaker, sub) with
  aliases, tuning, and deterministic rendering. No samples required.
- Renderer falls back to the synth kit when a sample is not found, so
  'audx render "kick 4/4"' makes sound with zero setup; --sample is optional.
- New commands: 'audx demo' (renders a full multi-track beat) and
  'audx synths' (lists built-in voices).
- Engine: import sounddevice lazily so the CLI and all offline features work
  without the PortAudio system library; add SynthVoice + synth fallback to the
  live callback; tidy types.
- Types: mypy now checks the whole package cleanly (was 4 files); fix real
  errors in midi/web/calibration/voice/groove; add types-requests.
- Add test_synth, test_render_synth, test_cli, test_more_coverage covering
  the synth kit, synth-fallback rendering, MIDI export, project round-trips,
  DSL operators, and the offline CLI surface. Coverage 27%→43%.
- Fix 'audx mix set <ch> gain -3': allow dash-leading values (ignore_unknown_options)
  so documented negative-dB usage works.
- Autouse fixtures reset the process-global pattern engine between CLI tests.
- Raise CI coverage gate 25→40.
…page

- Rewrite README around the 10-second zero-config demo and the built-in synth
  kit; add badges, DSL reference table, grouped command overview, honest
  'what it isn't yet' section.
- Add LICENSE (MIT), CHANGELOG.md (Keep a Changelog), CONTRIBUTING.md.
- Add docs/getting-started.md, docs/synth-kit.md and a docs index.
- Add site/ static landing page (dark terminal aesthetic, no build step).
CI was pip-installing an unpinned mypy (2.1.0) and using flake8, so it
diverged from the project's locked tools and failed on stricter
var-annotated/unused-ignore inference that 'uv run mypy' (1.20.2) doesn't
report. Run ruff, mypy and pytest through uv with --all-extras so CI matches
local exactly; drop flake8. fail-fast: false to see all matrix results.
…ideo + PyPI release

Product (team):
- synth: 6 melodic voices (bass, pluck, stab, keys/ep, saw, sine) — band-limited
  saws, pitched via tune; bass is now its own voice. Kit is 20 voices total.
- arrangement: Song/Section model + render_song; new 'audx song render|info'
  build multi-section tracks (intro/verse/drop/outro) from a JSON spec.
- ui: live finger-drum pads (w e r a s d f z x c u i o) trigger synth voices
  on dedicated channels while a project is open.

Marketing:
- Animated terminal demo GIF in README (scripts/make-demo-gif.py, Pillow-only).
- Runnable Remotion promo project (marketing/remotion/) + rendered 1080p MP4
  and poster (marketing/promo/).

Packaging:
- Automated PyPI release via trusted publishing on v* tags (.github/workflows/release.yml);
  PUBLISHING.md updated for the uv build flow.

Tests 123→172, coverage 43%→46%; ruff + mypy clean across the package.
The old promo was static terminal text with hard cuts. Rebuilt it around what
audx actually is — a live instrument:
- build-beat.py renders a real audx beat (synth kit) and emits a frame-accurate
  data track (per-track 16-step grids + hit frames, master RMS envelope, waveform
  peaks) so the motion graphics are driven by the actual audio.
- AudxPromo.tsx: a live step-sequencer grid that fires on the beat with a moving
  playhead, per-track VU meters, and a waveform scope that sweeps in time — with
  the rendered beat as the MP4 soundtrack. Smooth eased crossfades, no hard cuts.
- Electric-neon palette on pure black (cyan/magenta/lime/amber/violet).
- README hero is now the neon sequencer poster linking to the 20s promo.

Rendered artifacts: marketing/promo/audx-promo.mp4 (1080p, with audio) + poster.
… + gradients

Neon + gradients read as cheap/generic. Researched premium audio-brand and motion
direction (Teenage Engineering's muted-palette + single-accent restraint; the 2025
monochrome counter-trend to gradients/glow) and rebuilt accordingly:
- Near-monochrome: warm off-white on true black, a SINGLE amber accent used only
  for the play position. No gradients, no glows, flat fills.
- Sequencer is now a Monome/TE-style monochrome LED matrix: hairline-outlined
  cells, off-white hits, amber only under the thin playhead rule. Flat waveform.
- Swiss layout: passe-partout frame, lowercase monospace labels, generous space.
- Calmer motion: one bezier easing, restrained fades, discrete caret blink.
- Flat monochrome also compresses far better — MP4 2.7MB (was ~10MB).

Refreshed marketing/promo/*.mp4 + poster and docs/assets/promo-poster.png.
- audx jam: real-time play from a MIDI controller or Push 2. Drums mode maps
  GM notes to drum voices and never leaves a pad silent; --chromatic plays a
  melodic voice across the keys. New audx/live.py (pure, tested mapping) wired to
  engine.play_synth (now supports tune_semitones).
- docs/playing-live.md: kid-friendly controller/Push 2 walkthrough; linked from
  README ('Play it live') and the docs index.
- Bump to 0.3.0 (pyproject, package.json, __init__, CHANGELOG cut).
- Push2Lights drives Push 2 pad LEDs over MIDI: SysEx colour-palette entries
  (8-bit RGB split to 7-bit LSB/MSB) + note-on (channel 0) lighting, per the
  official Push 2 MIDI spec. Byte-exact unit tests against the spec example.
- push2_pad_layout: a 13-voice drum kit on the bottom two rows, one colour each.
- audx jam auto-detects a Push 2, paints the kit and flashes each pad on hit
  (--no-lights to disable; lights are drums-mode only). New 'audx push2 lights'
  lights the kit on its own for a quick check.
- Bundle python-rtmidi so mido has a backend — midi/jam/Push 2 work out of the box.
- Port discovery degrades gracefully with no backend/device; guide + README + CHANGELOG updated.
@chrisschouk
chrisschouk merged commit ca26d6c into main Jun 21, 2026
4 checks passed
chrisschouk pushed a commit that referenced this pull request Jun 21, 2026
main advanced via PR #4 ("Make audx marketable"), which added the same foundational
files this branch already carries (synth.py, push2.py, arrangement.py, site/, …) in a
parallel version — producing add/add conflicts. This branch is a strict superset of
those files (it also rewrote the serve dashboard and added the whole web/ studio), so
the 5 conflicts (README.md, docs/README.md, docs/playing-live.md, site/index.html,
synth.py) were resolved to this branch's versions, which contain main's content plus
the web additions (browser callout, open --serve, percussion alias, second-screen doc).

Verified after merge: ruff + mypy clean, full Python suite green, 108 web tests pass,
tsc clean.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants