jam-buddy / docs /02-architecture.md
salgadev's picture
Sync from GitHub 6feaf31d
b2e4883 verified
|
Raw
History Blame Contribute Delete
11.8 kB
# 02 β€” Architecture
## System overview
PatternTalk is two services plus a shared data layer.
```
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ Browser β”‚
β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚
β”‚ β”‚ Next.js Web App (TypeScript) β”‚ β”‚
β”‚ β”‚ β”œβ”€ Voice UI (Web Speech API: STT + TTS) β”‚ β”‚
β”‚ β”‚ β”œβ”€ Prompt parser β”‚ β”‚
β”‚ β”‚ β”œβ”€ Pattern engine (loads templates) β”‚ β”‚
β”‚ β”‚ β”œβ”€ MIDI generator (@tonejs/midi) β”‚ β”‚
β”‚ β”‚ β”œβ”€ Audio context analyzer (Meyda/Essentia.js) β”‚ β”‚
β”‚ β”‚ β”œβ”€ Reaper Web Control client β”‚ β”‚
β”‚ β”‚ └─ Visual grid (optional, layered on top) β”‚ β”‚
β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
β”‚ HTTP β”‚ WebSocket
β–Ό β–Ό
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ Audio Service β”‚ β”‚ Reaper (user's machine) β”‚
β”‚ (Python, FastAPI) β”‚ β”‚ Web Control surface β”‚
β”‚ β”œβ”€ SA3 inference β”‚ β”‚ + ReaScript bridge β”‚
β”‚ β”œβ”€ LoRA loader β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
β”‚ └─ Sample generator β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
β”‚
β–Ό
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ Stable Audio 3 weights (local FS) β”‚
β”‚ + Brutal-drum LoRA adapter β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
```
## Services
### 1. Web app (`apps/web`)
**Stack:** Next.js 14+ (App Router), TypeScript, Tailwind CSS, Radix UI primitives, @tonejs/midi, Meyda.
**Responsibilities:**
- Render the voice-first UI
- Capture microphone input β†’ text via Web Speech API (`SpeechRecognition`)
- Speak responses via `SpeechSynthesis`
- Parse prompts β†’ structured requests (see [`docs/03-data-model.md`](03-data-model.md))
- Load pattern templates, generate MIDI in-browser
- Optional: analyze uploaded audio for BPM (Meyda/Essentia.js)
- Talk to Reaper via Web Control (when available)
- Talk to audio service for sample generation
**State management:** Zustand or React state. Avoid Redux. State is small.
**Why Next.js:**
- Server components let us split render work from interactive shell
- Easy deploy to Vercel
- TS support is first-class
- Accessibility ecosystem (react-aria, Radix) is mature
### 2. Audio service (`services/audio`)
**Stack:** Python 3.10+, FastAPI, PyTorch 2.x, diffusers/transformers for SA3.
**Responsibilities:**
- Load Stable Audio 3 weights (base model)
- Load LoRA adapter (brutal-drums)
- Run inference on prompts β†’ audio buffers
- Stream or return audio as WAV
- Cache generated samples (LRU + disk-backed)
**Why separate service:**
- Python ML ecosystem is non-negotiable for SA3
- Decoupling lets us run on different machines (Vega for dev, cloud GPU for demo)
- Browser can't run SA3 inference efficiently
- Service can be scaled or replaced without touching the web app
### 3. Reaper integration (in user's DAW)
**Stack:** Reaper Web Control surface (built-in HTTP server) + optional ReaScript.
**Responsibilities:**
- Expose project tempo (BPM), time signature, play state
- Accept MIDI files dropped onto tracks
**Why this is not a "service":**
- Reaper runs on the user's machine
- PatternTalk is a client to its Web Control API
- No persistent server-side integration needed
## Data flow
### Happy path: voice prompt β†’ MIDI + sample
```
User voice: "tupatupatupa on the hihat, 4 bars"
β”‚
β–Ό
[Web Speech API] ──text──▢ "tupatupatupa on the hihat, 4 bars"
β”‚
β–Ό
[Onomatopoeia matcher] ──▢ { onomatopoeia: "tupatupatupa", mappedTo: "skank-beat" }
β”‚
β–Ό
[Prompt parser] ──▢ {
β”‚ pattern: "skank-beat",
β”‚ bars: 4,
β”‚ tempo: null, // not specified
β”‚ timeSignature: "4/4", // default
β”‚ cymbalHint: "hi-hat upstrokes"
β”‚ }
β–Ό
[Tempo resolver] ──▢ tempo: 174
β”‚ (priority: explicit in prompt > Reaper project BPM > uploaded audio BPM > 120 default)
β–Ό
[Pattern engine] ──▢ MIDI events[] (loaded from skank-beat template, expanded to 4 bars at 174 BPM)
β”‚
β–Ό
[MIDI generator] ──▢ .mid file (Blob in browser)
β”‚
β”œβ”€β–Ά [Download to user]
β”‚
└─▢ [Audio service request]
POST /generate-sample
{ prompt: "skank beat, hi-hat upstrokes, brutal drums", duration: 8 }
β”‚
β–Ό
[SA3 + LoRA inference] ──▢ audio buffer
β”‚
β–Ό
[Response] ──▢ .wav file (Blob in browser)
β”‚
└─▢ [Download to user]
```
### Voice response flow
After generation, PatternTalk speaks back:
```
"Skank beat, hi-hat upstrokes on the upbeats, 4 bars at 174 BPM.
MIDI ready. Sample ready. Say 'play' to preview, 'regenerate' to try again,
or 'download' to save the MIDI."
```
### Reaper sync flow
```
Web app mounts β†’ checks for Reaper Web Control at localhost:8080
β”‚
β”œβ”€ present β†’ fetch /_/project/tempo β†’ use as tempo default
β”‚
└─ absent β†’ use uploaded audio BPM or 120 default
```
## Folder structure
```
patterntalk/
β”œβ”€β”€ apps/
β”‚ └── web/ # Next.js app
β”‚ β”œβ”€β”€ app/ # App Router pages
β”‚ β”‚ β”œβ”€β”€ page.tsx # Main voice UI
β”‚ β”‚ β”œβ”€β”€ library/ # Pattern library
β”‚ β”‚ └── layout.tsx
β”‚ β”œβ”€β”€ components/
β”‚ β”‚ β”œβ”€β”€ voice/ # Voice UI primitives
β”‚ β”‚ β”œβ”€β”€ grid/ # Visual grid (optional)
β”‚ β”‚ └── ui/ # Radix wrappers
β”‚ β”œβ”€β”€ lib/
β”‚ β”‚ β”œβ”€β”€ parser/ # Prompt + onomatopoeia parser
β”‚ β”‚ β”œβ”€β”€ patterns/ # Pattern engine + template loader
β”‚ β”‚ β”œβ”€β”€ midi/ # MIDI generation (@tonejs/midi)
β”‚ β”‚ β”œβ”€β”€ audio/ # Web Audio, Meyda analyzer
β”‚ β”‚ └── reaper/ # Reaper Web Control client
β”‚ β”œβ”€β”€ data/
β”‚ β”‚ β”œβ”€β”€ patterns/ # JSON pattern templates
β”‚ β”‚ └── onomatopoeia.json # Onomatopoeia mapping table
β”‚ β”œβ”€β”€ public/
β”‚ └── package.json
β”œβ”€β”€ services/
β”‚ └── audio/ # FastAPI service
β”‚ β”œβ”€β”€ sa3/ # SA3 wrapper
β”‚ β”‚ β”œβ”€β”€ inference.py
β”‚ β”‚ β”œβ”€β”€ lora.py
β”‚ β”‚ └── server.py
β”‚ β”œβ”€β”€ training/ # LoRA fine-tuning scripts
β”‚ β”‚ └── train_lora.py
β”‚ β”œβ”€β”€ models/ # SA3 base weights (gitignored)
β”‚ β”œβ”€β”€ loras/ # Trained LoRA adapters
β”‚ β”œβ”€β”€ cache/ # Generated sample cache
β”‚ └── requirements.txt
β”œβ”€β”€ data/
β”‚ └── training/ # Brutal drum samples for LoRA
β”‚ β”œβ”€β”€ oneshots/ # Kick, snare, china, etc.
β”‚ β”œβ”€β”€ loops/ # Short brutal loops
β”‚ └── manifest.yaml # Training data manifest
β”œβ”€β”€ docs/ # This directory
β”œβ”€β”€ scripts/
β”‚ β”œβ”€β”€ reaper/ # ReaScript helpers
β”‚ └── verify/ # Accessibility + smoke tests
β”œβ”€β”€ .github/
β”‚ └── workflows/ # CI (axe-core, lint, build)
β”œβ”€β”€ package.json # Workspace root
└── README.md
```
## Why monorepo
- Single repo for web + audio service + training data
- Shared types between TS and Python (via JSON Schema + codegen, or just hand-written TS interfaces mirrored in Pydantic)
- Single CI pipeline
- Easier to ship as one artifact at the demo
**Tooling:** pnpm workspaces + a simple Python venv per service. Avoid Turborepo/Nx overhead for a 2-day project.
## Deployment
| Component | Target |
|---|---|
| Web app | Vercel (free tier) |
| Audio service | RunPod / Vast.ai during hackathon, optional Fly.io / Modal for inference |
| Models + LoRA weights | HuggingFace Hub (public, for the LoRA at least) |
| Training data | Small dataset, commit directly to repo or HuggingFace dataset |
## Key technical decisions
### Decision 1: Voice-first, not visual-first
The voice UI is the primary surface. The visual grid is optional and layered.
**Rationale:** Differentiates from every other drum plugin, hits the accessibility theme head-on, and matches how drummers actually think.
### Decision 2: Pattern engine decoupled from audio engine
The pattern (MIDI events) is hand-coded from templates. The audio (samples) is generated by SA3.
**Rationale:** Your domain expertise lives in the patterns. SA3's strength is sample quality. Don't conflate them. Each can be evaluated independently.
### Decision 3: Cloud GPU for inference during demo
Vega 56 can run SA3 small but slowly. Use cloud for demo-day inference to guarantee snappy response.
**Rationale:** Live inference during a 3-minute demo is high-risk if hardware is slow. A $20 cloud spend buys reliability.
### Decision 4: Open weights and open code
MIT code, public LoRA weights, public training manifest.
**Rationale:** Stability challenge explicitly rewards "open development." Showing the weights and training data is itself part of the demo.
### Decision 5: Reaper-first DAW integration
Web Control surface + MIDI export, not a full VST/CLAP.
**Rationale:** Web Control + drag-MIDI is 80% of the value at 20% of the work. CLAP wrap is stretch.
## What this architecture doesn't do
- No multi-user real-time collaboration (out of scope for 2 days)
- No pattern saving to cloud accounts (localStorage only for now)
- No mobile-first UI (desktop browser is the target)
- No offline mode (Vega inference can run offline, but the demo assumes network for cloud inference)
- No AU plugin format (Mac pain, not worth it for the demo)
## Open architectural questions
1. **Where does the pattern library live?** Browser-only (static JSON) vs. served from the audio service? *Lean: browser-only, simpler.*
2. **Should variations be pre-generated or on-demand?** Pre-generated is faster demo, on-demand is more impressive. *Lean: pre-generate for safety, on-demand as a "show your work" feature.*
3. **Real-time WebSocket for inference progress, or just polling?** WebSocket is nicer UX, polling is simpler. *Lean: WebSocket if time, polling if not.*