Music Perception
FreeNot checkedAnalyzes audio files to extract exact, reproducible measurements like loudness, tempo, key, spectral balance, and clipping for LLM-based DAW control.
About
Analyzes audio files to extract exact, reproducible measurements like loudness, tempo, key, spectral balance, and clipping for LLM-based DAW control.
README
The ears of a DAW-control agent. An MCP server that turns an audio file into exact, reproducible facts a text LLM can act on — loudness, true peak, tempo, key, spectral balance, clipping.
text-LLM brain (DeepSeek/…) ── decides ──► reaper-mcp.render_to_wav(...) ──► take.wav
▲ │
└────────────── facts (JSON) ◄── music-perception-mcp.analyze_audio(take.wav)
The brain renders a WAV (e.g. via reaper-mcp's
render_to_wav), calls a tool here to perceive it, then decides the next
mixing action. This server is a 取数型 (data-fetch) MCP tool in prism-core
terms: it returns context, it does not act on the DAW.
Speaks newline-delimited JSON-RPC 2.0 on stdin/stdout — the same protocol as
reaper-mcp, so prism-core's mcp_client connects to it identically.
Scope: deterministic measurement only
This server measures. The numbers are exact and reproducible (same file → same answer), computed by signal-processing libraries, not by an AI model.
The deterministic tools (analyze_audio, measure_loudness) make no
subjective judgement — no "muddy/harsh/sad". Those come from ONE clearly
separated, non-deterministic tool, listen_subjective, backed by an audio LLM
(Gemini). Exact numbers and opinions are kept apart on purpose — their
trustworthiness and use differ. An empirical benchmark backs this split:
deterministic MIR tracks controlled spectral defects perfectly (Spearman ρ≈1.0)
while models don't (≤0.31); models judge mood/emotion decently (0.46–0.64 vs
human DEAM ratings) while MIR is blind. See the music-agent design docs.
Tools
analyze_audio(path)
One-stop analysis. Returns:
| Field | What you get | Library |
|---|---|---|
loudness.integrated_lufs |
Integrated loudness (ITU-R BS.1770 / EBU R128) | pyloudnorm |
loudness.loudness_range_lu |
Loudness range (dynamics), gated P95−P10 of short-term | pyloudnorm + numpy |
loudness.true_peak_dbtp |
True peak via 4× oversampling (catches inter-sample overs) | scipy |
loudness.sample_peak_db |
Raw sample peak | numpy |
tempo.bpm |
Estimated tempo | librosa |
key.key / key.mode / key.confidence |
Global musical key (Krumhansl-Schmuckler) | librosa |
spectral.bands_db_rel |
6-band energy balance (sub/bass/low-mid/mid/high-mid/high), relative dB | librosa |
spectral.centroid_hz / rolloff_hz |
Brightness measures | librosa |
clipping |
Digital full-scale clip count + first timestamps | numpy |
measure_loudness(path)
Loudness block only (integrated LUFS, range, true peak, sample peak). Skips librosa, so it's fast — use it for quick master-bus checks against a target (e.g. −14 LUFS for streaming).
Both take an absolute path, e.g. one returned by reaper-mcp's
render_to_wav. WAV is the expected input; any
libsndfile-readable format works
(FLAC/OGG/AIFF). MP3/M4A are not guaranteed — render to WAV first.
listen_subjective(path, question?) — the one non-deterministic tool
Holistic "listening" judgement via an audio LLM (Gemini): 0-100
muddy/harsh/sibilant/bright, valence/arousal in [-10,10], a mood word,
timestamped issues, a one-line overall. Optional question focuses it
("is the vocal sibilant?"). Use it for mood / holistic feel; use analyze_audio
for exact numbers.
Needs a key — set env before launching the server:
GEMINI_API_KEY— required.GEMINI_BASE_URL— optional; set it to use an OpenAI-compatible relay (e.g. PackyCodehttps://www.packyapi.com/v1, or OpenRouter). Unset → Google's native Gemini API.GEMINI_MODEL— defaultgemini-2.5-flash(usegemini-2.5-flash-liteto save).
Without a key it returns {configured:false, error} and the deterministic tools
keep working. Install a backend: pip install openai (relay) or google-genai
(native). It downsamples to 16 kHz mono, ≤20 s, before sending.
Capabilities and boundaries
What this server is good for — and where each number stops being trustworthy. Read this before acting on a value.
| Metric | Reliable for | Boundary / caveat |
|---|---|---|
| Integrated LUFS | Master/stem loudness vs a target; A/B before-after | Whole-file integrated; not a live/streaming meter |
| True peak (dBTP) | Catching inter-sample overs before a limiter ceiling | 4× oversample (BS.1770 minimum); a hair below dedicated 8× meters but well within practical tolerance |
| Loudness range (LU) | Rough dynamics / over-compression check | EBU-style short-term implementation; treat as indicative, not certified |
| Tempo (BPM) | Steady electronic / pop / rock | Unreliable on rubato, free time, ambient, or no clear beat — returns 0.0 when it finds no beat (honest, not an error) |
| Key | Single-key tonal material | One global key only — misses modulations/key changes; weak on atonal/percussive/sparse audio; major-vs-minor can flip on ambiguous tonality. Use confidence |
| Spectral bands | Comparing a mix against a reference curve ("too much 2–6 kHz vs the reference") | Relative energy (dB vs total), not an absolute/calibrated spectrum; not loudness-weighted |
| Clipping | Detecting digital full-scale clipping | Full-scale only (≥0.999); soft/analog-style clipping and inter-sample overs are not here — those show up as a high true_peak_dbtp |
Cross-cutting:
- Measurement vs opinion. The deterministic tools give exact numbers;
listen_subjectivegives the opinions ("muddy/harsh/sad") — separately, and non-deterministically. - Garbage in, garbage out. Feed it the actual render. The numbers describe exactly the file you pass, including its sample rate and channel layout.
- One global answer per file for tempo/key. For per-section analysis,
render that section (reaper-mcp
render_to_wavwith a time selection orregion:N) and analyze it separately.
Setup
pip install -r requirements.txt # numpy soundfile pyloudnorm librosa scipy
python server/test_server.py # offline self-test on a synthetic WAV
Register with an MCP client (e.g. prism-core / Claude Code) — add to your
mcp_servers.json / .mcp.json:
{
"mcpServers": {
"music-perception": {
"command": "python",
"args": ["A:\\Prismcode\\music-perception-mcp\\server\\music_perception_server.py"]
}
}
}
Dependencies & licensing
All dependencies are permissive (BSD/MIT/ISC) and pure-pip — no external
binary, no ffmpeg. They are confined to this server; the prism-core kernel
and the other MCP servers stay zero-dependency. Notably this avoids
madmom (non-commercial model weights) and Essentia (AGPL), so the stack
stays commercial-friendly.
Roadmap
separate_stems(path)— Demucs source separation (heavy; CPU-slow). Lets you measure each instrument's loudness/masking.- (done)
listen_subjective— the subjective/mood layer, see above.
Installing Music Perception
This server has no published package — it is built from source. Open the repository and follow its README.
▸ github.com/AnqiPinku/music-perception-mcpFAQ
Is Music Perception MCP free?
Yes, Music Perception MCP is free — one-click install via Unyly at no cost.
Does Music Perception need an API key?
No, Music Perception runs without API keys or environment variables.
Is Music Perception hosted or self-hosted?
Self-hosted: the server runs locally on your machine via the install command above.
How do I install Music Perception in Claude Desktop, Claude Code or Cursor?
Open Music Perception on unyly.org, pick your client tab (Claude Desktop, Claude Code, Cursor) and press Install — the config is generated automatically, no JSON editing.
Related MCPs
Omni Video
An MCP server that transforms LLM-enabled IDEs into professional video editors by pre-processing footage into text proxies, generating motion graphics via HTML/
by buildwithtazaARA
Generate images, video and audio from any AI agent — one connector.
by ARAYouTube
Transcripts, channel stats, search
by YouTubeEverArt
AI image generation using various models.
by modelcontextprotocolCompare Music Perception with
Not sure what to pick?
Find your stack in 60 seconds
Author?
Embed badge for your README
Browse similar
All media MCPs
