beatlyzer-mcp
by Profazia
README.md
# beatlyzer
**Audio analysis that machines (and humans) can read.**
Point `beatlyzer` at an `.mp3` (or `.wav`, `.flac`, `.ogg`, `.m4a`, …) and it produces:
- a **structured, LLM-friendly JSON** description of the track,
- a **rich terminal summary**,
- an **annotated multi-panel visualization** (PNG) marking **beat drops**,
**volume surges**, **high tones**, brightness, structure and the spectrogram,
- an optional **Markdown report**.
It's designed so a vision- or text-capable AI model can "understand" a song:
what it sounds like, how it's structured, where the energy peaks, and exactly
*when* the interesting moments happen.
---
## Example
`beatlyzer demo.wav` produces this four-panel analysis:

Alongside the PNG it writes a machine-readable
[`demo.beatlyzer.json`](demo.beatlyzer.json) and a
[`demo.beatlyzer.md`](demo.beatlyzer.md) report — both included here. Regenerate
the demo input yourself with `python scripts/make_sample.py demo.wav`.
---
## What it detects
| Signal | How | Output |
| --- | --- | --- |
| **Tempo & beats** | librosa beat tracking | BPM + beat grid |
| **Key** | Krumhansl-Schmuckler chroma correlation | e.g. `F minor` |
| **Beat drops** | sharp rises into loud, **bass-heavy** passages, snapped to beats | timeline events |
| **Volume surges** | crescendos / hits (loudness jumps that aren't drops) | timeline events |
| **High tones** | spectral-centroid + treble-energy peaks | timeline events |
| **Loudness & dynamics** | RMS in dB relative to peak | avg / peak / dynamic range |
| **Brightness** | spectral centroid | dark ↔ airy |
| **Structure** | agglomerative clustering of timbre+harmony | intro → build → drop → outro |
---
## Install
Requires Python 3.9+ (3.10+ for the MCP server). For **mp3/m4a/aac** decoding
you need **ffmpeg** on your PATH (`sudo dnf install ffmpeg` /
`brew install ffmpeg` / `apt install ffmpeg`).
### As a global CLI tool — recommended, no venv to manage
The cleanest way to get `beatlyzer` (and `beatlyzer-mcp`) available *everywhere*
is [pipx](https://pipx.pypa.io) or [uv](https://docs.astral.sh/uv/), which each
install the tool into their own isolated environment and put the commands on
your PATH — you never create or activate a virtualenv yourself:
```bash
# pipx
pipx install 'git+https://github.com/Profazia/beatlyzer.git'
pipx inject beatlyzer mcp # add the MCP server extra
# or uv
uv tool install 'beatlyzer[mcp] @ git+https://github.com/Profazia/beatlyzer.git'
# run once without installing anything permanent:
uvx --from 'git+https://github.com/Profazia/beatlyzer.git' beatlyzer song.mp3
```
> **Note:** `librosa`/`numba` may lag the newest Python release. If a build
> fails, pin the interpreter: `pipx install --python python3.12 ...`.
### From a clone (development)
```bash
pip install -e ".[dev,mcp]" # editable install with test + MCP deps
```
This installs the `beatlyzer` and `beatlyzer-mcp` commands.
---
## Usage
```bash
# analyze a file — writes <name>.beatlyzer.{png,json,md} next to it
beatlyzer song.mp3
# choose an output directory and open the image when done
beatlyzer track.wav -o out/ --open
# only the machine-readable JSON, printed to stdout, no files, no chatter
beatlyzer mix.flac --format json --print-json --quiet
```
Don't have a file handy? Generate a demo track:
```bash
python scripts/make_sample.py sample.wav
beatlyzer sample.wav --open
```
### Options
```
-o, --output-dir DIR Where to write outputs (default: next to the input)
-f, --format CHOICE all | png | json | md | none (default: all)
--stem NAME Base name for output files (default: input name)
--sr INT Analysis sample rate (default: 22050)
--dpi INT PNG resolution (default: 140)
--open Open the PNG when finished
--quiet Suppress the terminal summary
--print-json Print the JSON summary to stdout
-V, --version Show version
```
Run as a module too: `python -m beatlyzer song.mp3`.
---
## The AI-readable JSON
`<name>.beatlyzer.json` follows the `beatlyzer.analysis/v1` schema. Feed it
straight to an LLM — it's self-describing (see the `ai_notes` field):
```jsonc
{
"schema": "beatlyzer.analysis/v1",
"metadata": { "duration_hms": "0:24", "tempo_bpm": 128.0, "estimated_key": "A minor", ... },
"loudness": { "average_db": -18.4, "dynamic_range_db": 31.2, "description": "wide dynamics" },
"brightness":{ "average_centroid_hz": 2680.0, "description": "balanced" },
"sections": [ { "label": "intro", "start_hms": "0:00", "energy_level": "low" }, ... ],
"events": [
{ "time_hms": "0:09", "type": "beat_drop", "strength_0_1": 1.0, "bass_share": 0.71,
"description": "Beat drop at 0:09 — energy surges into a loud, bass-heavy section." },
{ "time_hms": "0:17", "type": "high_tone", "strength_0_1": 0.93, "centroid_hz": 6011.0, ... }
],
"energy_profile": [ { "t": 0.0, "energy": 0.05, "loudness_db": -34.1, "brightness": 0.2 }, ... ],
"summary_text": "This 0:24 track is a high-energy piece at 128 BPM in A minor. ...",
"ai_notes": "Times are seconds from the start. 'loudness' is dB relative to the track's peak ..."
}
```
## The visualization
`<name>.beatlyzer.png` is a four-panel figure sharing one time axis:
1. **Waveform** with shaded structural sections and a faint beat grid.
2. **Loudness (dB)** with **beat drops** (red) and **volume surges** (orange).
3. **Brightness** (spectral centroid) with **high-tone** peaks (purple).
4. **Mel spectrogram** with drop lines overlaid.
---
## Use it as an MCP server
beatlyzer ships an [MCP](https://modelcontextprotocol.io) server so AI agents —
Claude Code, Claude Desktop, Cursor, etc. — can analyze audio directly. It runs
over stdio and exposes three tools:
| Tool | Returns |
| --- | --- |
| `analyze_audio(file_path, sample_rate=22050)` | full structured JSON summary |
| `describe_audio(file_path)` | a short prose description |
| `visualize_audio(file_path, output_path=None, dpi=140)` | the annotated PNG, as an image the model can see |
Make sure the `mcp` extra is installed (`pipx inject beatlyzer mcp`, or
`pip install 'beatlyzer[mcp]'`), then register the server.
**Claude Code:**
```bash
claude mcp add beatlyzer beatlyzer-mcp
```
**Claude Desktop / any MCP client** (`claude_desktop_config.json` or equivalent):
```json
{
"mcpServers": {
"beatlyzer": {
"command": "beatlyzer-mcp"
}
}
}
```
If `beatlyzer-mcp` isn't on the client's PATH, use the absolute path (e.g.
`~/.local/bin/beatlyzer-mcp`, or the one printed by `pipx list`). A ready-made
snippet lives in [`examples/mcp-config.json`](examples/mcp-config.json).
Then just ask the model things like *"analyze ~/Music/track.mp3 and tell me
where the beat drops are"* or *"visualize this song."*
---
## As a library
```python
from beatlyzer import analyze_file
from beatlyzer.report import build_result_dict
result = analyze_file("song.mp3")
print(result.summary_text)
print([e.time for e in result.drops])
doc = build_result_dict(result) # the JSON-ready dict
```
---
## How the detection works (short version)
Every detector smooths a feature track, measures how it **transitions**
(mean energy *after* a moment minus mean *before*), then keeps prominent,
well-separated peaks and snaps them to the nearest beat:
- **Drops** blend overall RMS with sub-250 Hz **bass** energy and additionally
require the landing passage to be genuinely loud — a build-up that fizzles
isn't a drop.
- **Surges** track loudness jumps and are de-duplicated against drops.
- **High tones** combine the spectral centroid with the >4 kHz energy share.
These are transparent heuristics, not a trained model — fast, dependency-light,
and explainable. Tune thresholds in `src/beatlyzer/events.py`.
## Development
```bash
pip install -e ".[dev]"
pytest
```
## License
MIT — see [LICENSE](LICENSE).
This server cannot be deployed
Maintenance
ActivityInactive
ResponsivenessNo issues