Skip to main content

Audio analysis CLI: SuperFlux onsets + RNN/DBN beats and downbeats, with streaming NDJSON logs.

Project description

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 — madmom on PyPI is stale. The last PyPI release of madmom (0.16.1, 2018) does not work with NumPy >= 2. This package declares madmom as a regular dependency for PyPI compliance, but in practice you'll want the current main branch from CPJKU/madmom. When using uv, the bundled [tool.uv.sources] in pyproject.toml already does this for you. With plain pip, 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-defaults for the schema.
  • --write-viewer — also write <stem>.png, <stem>.html, <stem>.json next 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 knobs
  • onset.{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 pass
  • beats.beats_per_bar — candidate meters for the DBN downbeat tracker
  • viewer.{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.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

zigify_madmom-1.0.0.tar.gz (41.7 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

zigify_madmom-1.0.0-py3-none-any.whl (42.5 kB view details)

Uploaded Python 3

File details

Details for the file zigify_madmom-1.0.0.tar.gz.

File metadata

  • Download URL: zigify_madmom-1.0.0.tar.gz
  • Upload date:
  • Size: 41.7 kB
  • Tags: Source
  • Uploaded using 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}

File hashes

Hashes for zigify_madmom-1.0.0.tar.gz
Algorithm Hash digest
SHA256 d0ea312f8067612792d8e500e20d2c326418330a64973549d02fe452c23a5e6b
MD5 8942f382287d213a77e61f37a15c23ae
BLAKE2b-256 c74344e7ab0b860ab1d08bc07201ba7b4bec9dba4a17fb64cab796e0aba01100

See more details on using hashes here.

File details

Details for the file zigify_madmom-1.0.0-py3-none-any.whl.

File metadata

  • Download URL: zigify_madmom-1.0.0-py3-none-any.whl
  • Upload date:
  • Size: 42.5 kB
  • Tags: Python 3
  • Uploaded using 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}

File hashes

Hashes for zigify_madmom-1.0.0-py3-none-any.whl
Algorithm Hash digest
SHA256 523de47b103bf1de7b0ddb236578c3bec49a78646363b379d7232a38c5a8341d
MD5 7fcd1752f771344832843d7744e43479
BLAKE2b-256 72222992962f70e7304c7862ce9ed134fd542c690233433dc47eb61548a2f4ce

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page