beatlyzer-mcp
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 and a
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. |
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 or 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:
# 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.mp3Note:
librosa/numbamay lag the newest Python release. If a build fails, pin the interpreter:pipx install --python python3.12 ....
From a clone (development)
pip install -e ".[dev,mcp]" # editable install with test + MCP depsThis installs the beatlyzer and beatlyzer-mcp commands.
Usage
# 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 --quietDon't have a file handy? Generate a demo track:
python scripts/make_sample.py sample.wav
beatlyzer sample.wav --openOptions
-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 versionRun 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):
{
"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:
Waveform with shaded structural sections and a faint beat grid.
Loudness (dB) with beat drops (red) and volume surges (orange).
Brightness (spectral centroid) with high-tone peaks (purple).
Mel spectrogram with drop lines overlaid.
Use it as an MCP server
beatlyzer ships an MCP server so AI agents — Claude Code, Claude Desktop, Cursor, etc. — can analyze audio directly. It runs over stdio and exposes three tools:
Tool | Returns |
| full structured JSON summary |
| a short prose description |
| 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:
claude mcp add beatlyzer beatlyzer-mcpClaude Desktop / any MCP client (claude_desktop_config.json or equivalent):
{
"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.
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
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 dictHow 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
pip install -e ".[dev]"
pytestLicense
MIT — see LICENSE.