zigify-madmom
Audio analysis CLI: percussive onsets, beats, and downbeats for a single
audio file, packaged as a uvx-runnable tool with a streaming JSON
protocol so other processes (Node, Bun, anything) can consume progress
events live and capture the final result cleanly.
Built on madmom: SuperFlux ODF on the HPSS percussive component, adaptive peak picking, density-aware NMS, an energy-rescue pass, and madmom's RNN/DBN beat & downbeat trackers.
Install / run
# one-shot, no install
uvx zigify-madmom path/to/audio.mp3 > result.json
# project-local (in a uv project)
uv add zigify-madmom
# pip
pip install zigify-madmom
Heads up —
madmomon PyPI is stale. The last PyPI release of madmom (0.16.1, 2018) does not work with NumPy >= 2. This package declaresmadmomas a regular dependency for PyPI compliance, but in practice you'll want the currentmainbranch from CPJKU/madmom. When usinguv, the bundled[tool.uv.sources]inpyproject.tomlalready does this for you. With plainpip, install the git version yourself first:pip install "madmom @ git+https://github.com/CPJKU/madmom" pip install zigify-madmom
CLI
zigify-madmom AUDIO [--config FILE] [--write-viewer] [--viewer-dir DIR]
[--stdin-suffix .wav] [--print-defaults]
AUDIO— path to an audio file, or-to read raw bytes from stdin (buffered to a temp file because madmom's beat trackers expect a path).--config FILE— JSON config file. Keys are deep-merged over the defaults; omit to use defaults. See--print-defaultsfor the schema.--write-viewer— also write<stem>.png,<stem>.html,<stem>.jsonnext to the audio. The HTML is a self-contained player with a synced cursor over the saved plot.--viewer-dir DIR— override where viewer files go (default: alongside the audio file).--stdin-suffix— extension for the temp file when reading stdin (default.wav); set to match your input format if needed.--print-defaults— print the default config as JSON and exit. Use as a starting template:zigify-madmom --print-defaults > my-config.json
Examples
# defaults, capture payload to a file
zigify-madmom audio.mp3 > out.json
# custom config + viewer files
zigify-madmom audio.mp3 --config tuned.json --write-viewer
# pipe from ffmpeg
ffmpeg -i input.flac -f wav - | zigify-madmom - --stdin-suffix .wav > out.json
Streaming protocol
The CLI splits its output across two streams so a parent process can display progress live and still parse the final result:
| Stream | Format | Purpose |
|---|---|---|
| stderr | NDJSON, one per line | Progress / log events |
| stdout | One JSON object | Final analysis payload |
Every stderr line is a complete JSON object with at minimum level and
msg, plus optional fields like stage, counters, paths. Errors come
through the same channel with "level": "error" and a traceback field;
the process exits non-zero.
// stderr — observed live
{"level":"info","msg":"Loading audio","stage":"load","path":"audio.mp3"}
{"level":"info","msg":"Computing SuperFlux ODF","stage":"odf","hpss":true}
{"level":"info","msg":"Onset NMS complete","stage":"onset_nms","primary":560,"raw":949,"culled":389}
{"level":"info","msg":"Energy rescue complete","stage":"onset_rescue","energy":360}
{"level":"info","msg":"Tracking beats","stage":"beats"}
{"level":"info","msg":"Tracking downbeats","stage":"downbeats"}
{"level":"info","msg":"Tempo computed","stage":"tempo","bpm_med":100.0,"bpm_min":71.4,"bpm_max":107.1}
{"level":"info","msg":"Done","stage":"done"}
// stdout — single JSON object at end
{
"meta": {
"file": "audio.mp3",
"duration_s": 242.98,
"fps": 100,
"mode": "HPSS+SuperFlux",
"tempo_bpm": 100.0,
"tempo_range_bpm": [71.4, 107.1],
"beats_per_bar_candidates": [4, 4]
},
"bars": [
{ "index": 1, "start_s": 0.123, "end_s": 2.521,
"beats": [0.123, 0.722, 1.323, 1.922],
"rms_db": -18.4, "odf_energy": 0.27 }
],
"beats": [0.123, 0.722, 1.323, /* ... */],
"downbeats": [0.123, 2.521, /* ... */],
"onsets": [{ "t": 0.42, "strength": 0.61 }, /* ... */],
"onsets_energy": [{ "t": 0.45, "strength": 0.18 }, /* ... */]
}
Consuming from TypeScript
import { spawn } from "node:child_process";
import { createInterface } from "node:readline";
const proc = spawn("zigify-madmom", ["audio.mp3", "--config", "cfg.json"]);
const rl = createInterface({ input: proc.stderr });
rl.on("line", (line) => {
const evt = JSON.parse(line);
console.log(`[${evt.stage ?? evt.level}] ${evt.msg}`);
});
let stdout = "";
proc.stdout.on("data", (chunk) => (stdout += chunk));
proc.on("close", (code) => {
if (code !== 0) throw new Error(`exit ${code}`);
const payload = JSON.parse(stdout);
// payload.meta, payload.bars, payload.beats, ...
});
Exit codes
| Code | Meaning |
|---|---|
| 0 | Success |
| 1 | Analysis error (see stderr traceback) |
| 2 | Bad arguments / missing file / config parse error |
| 130 | Interrupted (Ctrl+C) |
Config
The full default config is available via zigify-madmom --print-defaults.
Top-level keys:
fps— analysis frame rate (default 100)use_hpss— gate the ODF on the HPSS percussive component (default true)hpss.{masking_p, harmonic_filter, percussive_filter}— HPSS knobsonset.{offset, pre_avg_s, post_avg_s, combine_s, nms_quiet_s, nms_loud_s, rescue_min_density, rescue_min_strength, rescue_min_spacing_s}— peak picking, density-adaptive NMS, and the energy-rescue passbeats.beats_per_bar— candidate meters for the DBN downbeat trackerviewer.{png_px_per_minute, png_dpi, png_min_width_in}— only used with--write-viewer
Partial configs are deep-merged; only override what you want to change.
{
"use_hpss": false,
"onset": { "offset": 0.15, "nms_loud_s": 0.25 }
}
Programmatic use
from zigify_madmom import analyze, load_config
cfg = load_config(None) # defaults; pass a path to override
result = analyze("audio.mp3", cfg)
payload = result["payload"]
analyze() accepts an optional log callable for custom progress
reporting: analyze(path, cfg, log=lambda msg, **kw: ...).
Development
git clone <repo>
cd zigify-madmom
uv sync
uv run zigify-madmom audio/your-track.mp3
License
MIT — see LICENSE.
Metadata
Release files for zigify-madmom 1.1.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| zigify_madmom-1.1.0.tar.gz | 30.4 MB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| zigify_madmom-1.1.0-cp312-cp312-macosx_11_0_arm64.whl | CPython 3.12 | CPython 3.12 | macOS 11.0+ ARM64 | Details |
Total release size: 61.3 MB
Release files / zigify_madmom-1.1.0.tar.gz
| Download URL | zigify_madmom-1.1.0.tar.gz |
|---|---|
| Size | 30.4 MB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
237f7f3872d9a49f928883f9558d43195b793541498238ba7e4f229ee09800e5
|
|
BLAKE2b-256 checksum How to use checksums |
e084aa093dbeba09f3119d5b2ce541245684c44020d41671d0237273e65a00bb
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
uv/0.10.9 {"installer":{"name":"uv","version":"0.10.9","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
|
Release files / zigify_madmom-1.1.0-cp312-cp312-macosx_11_0_arm64.whl
| Download URL | zigify_madmom-1.1.0-cp312-cp312-macosx_11_0_arm64.whl |
|---|---|
| Size | 30.9 MB |
| Tags | CPython 3.12 macOS 11.0+ ARM64 |
|
SHA-256 checksum How to use checksums |
f5bf03359f756c341cf31869116f3aad833c158fc60afa4e7c33485133676b74
|
|
BLAKE2b-256 checksum How to use checksums |
f8073fc4e24f0d5279d204adbbd4c34fbbd54b8a301794091165ffc8427b9838
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
uv/0.10.9 {"installer":{"name":"uv","version":"0.10.9","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
|