# ClickTrack A self-hosted, open-source click track generator for cover bands. Search any song, browse community-contributed tempo maps, and download a metronomic WAV file — accented downbeats, time signature changes, and tempo ramps included. --- ## Features - **Song search** via MusicBrainz, cached locally in PostgreSQL - **Community tempo maps** in the open CTP (Click Track Protocol) format - **WAV generation** — 44.1 kHz, 16-bit PCM, mono; 880 Hz accented / 440 Hz unaccented clicks - **Count-in** — configurable 1–8 bar count-in prepended before bar 1 - **Tempo ramps** — linear BPM interpolation beat-by-beat for ritardando/accelerando sections - **Federated registry** — pull CTP files from any public Git repo on a cron schedule - **Self-hosted** — single `docker compose up` gets you running --- ## Quick Start (Docker) ### 1. Clone and configure ```bash git clone https://github.com/your-org/clicktrack.git cd clicktrack cp .env.example .env # Edit .env — at minimum set a strong POSTGRES_PASSWORD ``` ### 2. Start the stack ```bash docker compose up -d --build ``` This starts: | Container | Role | |---|---| | `app` | Next.js 14 (port 3000, proxied via nginx) | | `postgres` | PostgreSQL 16 with persistent volume | | `redis` | Redis 7 for caching | | `nginx` | Reverse proxy on ports 80/443 | ### 3. Open the app Navigate to `http://localhost` (or your server's IP/domain). ### 4. Enable registry sync (optional) Set `REGISTRY_REPO` in `.env` to a public GitHub repo URL containing CTP files, then: ```bash docker compose --profile registry up -d ``` The `registry-sync` container will pull and import CTP files every `REGISTRY_SYNC_INTERVAL` seconds (default: 1 hour). --- ## Development Setup ```bash # Prerequisites: Node 20+, a local PostgreSQL 16, Redis 7 cp .env.example .env # Update DATABASE_URL and REDIS_URL to point at your local services npm install # Apply schema psql $DATABASE_URL -f lib/db/schema.sql # Start dev server with hot reload npm run dev ``` Or with Docker (hot-reload via volume mount): ```bash docker compose -f docker-compose.yml -f docker-compose.dev.yml up --build ``` --- ## CTP — Click Track Protocol CTP is an open JSON format for describing a song's complete tempo map. A CTP file is a plain `.ctp.json` file that can be version-controlled and shared. ### Full example ```json { "version": "1.0", "metadata": { "title": "Bohemian Rhapsody", "artist": "Queen", "mbid": "b1a9c0e0-9c4a-4a6b-8b5a-1234567890ab", "duration_seconds": 354, "contributed_by": "freddie_fan", "verified": false, "created_at": "2026-04-01T00:00:00Z" }, "count_in": { "enabled": true, "bars": 1, "use_first_section_tempo": true }, "sections": [ { "label": "Intro (Ballad)", "start_bar": 1, "bpm": 72.0, "time_signature": { "numerator": 4, "denominator": 4 }, "transition": "step" }, { "label": "Rock Section", "start_bar": 45, "bpm": 152.0, "time_signature": { "numerator": 4, "denominator": 4 }, "transition": "step" }, { "label": "Outro (slowing)", "start_bar": 120, "bpm_start": 152.0, "bpm_end": 72.0, "time_signature": { "numerator": 4, "denominator": 4 }, "transition": "ramp" } ] } ``` ### Schema rules | Field | Type | Notes | |---|---|---| | `version` | `"1.0"` | Must be exactly `"1.0"` | | `metadata.mbid` | UUID or `null` | MusicBrainz Recording ID | | `metadata.duration_seconds` | number | Total song duration (excluding count-in) | | `count_in.bars` | 1–8 | Number of count-in bars | | `sections[*].start_bar` | integer ≥ 1 | Must start at 1, strictly ascending | | `sections[*].transition` | `"step"` \| `"ramp"` | Step = instant change; ramp = linear interpolation | | `sections[*].bpm` | 20–400 | Only for `step` sections | | `sections[*].bpm_start` / `bpm_end` | 20–400 | Only for `ramp` sections | | `sections[*].time_signature.denominator` | 1, 2, 4, 8, 16, 32 | Must be a power of 2 | ### Click sounds | Beat | Frequency | Amplitude | |---|---|---| | Beat 1 (downbeat) | 880 Hz | Accented | | Other beats | 440 Hz | Normal | Both use a 12 ms sine wave with exponential decay (`e^(-300t)`). --- ## Community Registry The registry is a public Git repository containing `.ctp.json` files organised by artist: ``` registry-repo/ q/ queen/ b1a9c0e0-9c4a-4a6b-8b5a-1234567890ab.ctp.json # Bohemian Rhapsody t/ the-beatles/ ... ``` ### Contributing a tempo map 1. Fork the community registry repo. 2. Create a `.ctp.json` file for your song. Use the schema above. 3. Validate your file: paste the JSON into a CTP validator (coming soon) or use the API: ```bash curl -X POST http://localhost/api/tracks \ -H "Content-Type: application/json" \ -d @your-song.ctp.json ``` 4. Open a pull request to the registry repo. Once merged, any ClickTrack instance syncing that registry will import your map automatically. --- ## API Reference ### `GET /api/songs?q=&limit=` Search for songs. Hits local DB first, falls back to MusicBrainz. **Response:** ```json { "songs": [...], "total": 5 } ``` ### `GET /api/tracks?mbid=` List all community tempo maps for a song. ### `POST /api/tracks` Submit a new CTP document. Body must be a valid CTP JSON. **Response:** `201 Created` with the stored map record. ### `GET /api/generate?id=&count_in=true` Generate and download a WAV click track. - `id` — UUID of the tempo map (from `/api/tracks`) - `count_in` — `true` or `false` (default: `true`) Returns `audio/wav` with `Content-Disposition: attachment`. --- ## Architecture ``` Browser └── Next.js App Router (app/) ├── (web)/page.tsx — song search ├── (web)/track/[id]/ — track page + player └── api/ ├── songs/ — search + MB integration ├── tracks/ — CTP CRUD └── generate/ — WAV rendering lib/ ├── ctp/ │ ├── schema.ts — TypeScript types │ ├── validate.ts — Zod validation │ └── render.ts — CTP → WAV (Node.js) ├── db/client.ts — pg Pool + query helpers ├── musicbrainz/client.ts — rate-limited MB API └── registry/sync.ts — Git registry pull + upsert ``` --- ## Environment Variables See [`.env.example`](.env.example) for all variables with descriptions. | Variable | Required | Default | Description | |---|---|---|---| | `DATABASE_URL` | Yes | — | PostgreSQL connection string | | `REDIS_URL` | Yes | — | Redis connection URL | | `NEXT_PUBLIC_APP_NAME` | No | `ClickTrack` | UI display name | | `REGISTRY_REPO` | No | — | GitHub repo URL for CTP registry | | `REGISTRY_SYNC_INTERVAL` | No | `3600` | Sync interval in seconds | | `MUSICBRAINZ_USER_AGENT` | No | built-in | User-Agent for MB API requests | --- ## License MIT