Skip to main content

Modernized, from-scratch reimplementation of madmom's inference-relevant audio-DSP and beat/downbeat-tracking algorithms

Project description

madmom-infer

A from-scratch, modernized reimplementation of madmom's inference-relevant algorithms

PyPI License: BSD-2-Clause Python 3.9+


Why this exists

madmom is a well-regarded MIR (Music Information Retrieval) / audio-DSP research library out of CPJKU (Johannes Kepler University, Linz) and OFAI (Vienna). Its algorithms -- spectrogram feature extraction, beat and downbeat tracking, onset detection, tempo estimation, and more -- are still widely used and cited. But madmom's PyPI release is roughly 8 years stale, ships compiled Cython extensions, and is difficult or impossible to install cleanly on modern Python / numpy / scipy versions.

madmom-infer re-derives madmom's inference-relevant algorithms from scratch against current Python tooling. It is an independent reimplementation, not an official fork -- see NOTICE. It does not reuse or redistribute any of madmom's original source code; it reimplements the published algorithms.

Scope

This project targets madmom's inference code only:

  • Signal processing and feature extraction (framing, STFT, filterbanks, log-spectrograms)
  • Decoding algorithms (Viterbi-based HMM/DBN beat and downbeat tracking)
  • Onset/tempo/chord/key/note feature extraction (Phase 2, see below)

Out of scope, forever:

  • madmom.evaluation.* -- madmom's F-measure/precision-recall research evaluation metrics (~4447 lines). This is tooling for scoring MIR research output, not for inference, and is not part of this project's scope under any phase.
  • Training-only code. Madmom itself has essentially no gradient-based training code to port (its neural-net layers are forward-inference-only already).

Dual backend design

  • numpy backend (default, required): the reference implementation. Verified bit-identical to original madmom via golden-fixture tests -- recordings of real madmom output, checked against this port's output. This is the same testing philosophy used by the sibling all-in-one-infer package's pure-Python NATTEN replacement.
  • torch backend (optional, via the torch extra): GPU-accelerated batch processing. Most valuable for the spectrogram/STFT stage, which batches trivially across frames. The Viterbi decoder is an inherently sequential, per-frame recursion, so GPU gains there are expected to be marginal -- we're not going to oversell that in these docs.

Install the optional torch backend with:

pip install "madmom-infer[torch]"

Roadmap

Phase 1 -- complete

Driven by the sibling all-in-one-infer package's needs. All of the original madmom code in this phase is pure numpy/scipy (no Cython), so most of it was a near-mechanical port, verified against madmom via golden fixtures:

  • Signal, FramedSignalProcessor (madmom_infer/audio/signal.py)

  • ShortTimeFourierTransformProcessor (madmom_infer/audio/stft.py)

  • Filterbank construction (madmom_infer/audio/filters.py)

  • FilteredSpectrogramProcessor, LogarithmicSpectrogramProcessor (madmom_infer/audio/spectrogram.py)

  • Processor / SequentialProcessor composition (madmom_infer/processors.py)

  • A numpy reimplementation of madmom's Cython Viterbi decoder (madmom/ml/hmm.pyx -> HiddenMarkovModel.viterbi()), the phase-1 centerpiece (madmom_infer/ml/hmm.py), plus the bar-length state-space and DBN downbeat tracker that consume it (madmom_infer/features/beats_hmm.py, madmom_infer/features/downbeats.py).

    This is feasible in pure numpy because the beat/downbeat state space is small (~11k-15k states per bar-length HMM), transitions are sparse (~1-2 incoming edges per state), and the recursion is log-domain -- well within numpy vectorization territory, no Cython/C/GPU required.

Every stage above has a golden-fixture test (tests/test_*.py, 84 tests total) proving bit-identical output against a real, compiled madmom 0.17.dev0 install -- except the one stage where bit-identity is bounded by BLAS library non-associativity rather than achievable in principle: the filterbank matrix-multiply (np.dot(spectrogram, filterbank)) rounds differently by a handful of float32 ULPs depending on which OpenBLAS build numpy resolves to. This is proven, not assumed -- tests/test_spectrogram.py's test_filtered_spectrogram_algorithm_is_exact_under_original_blas exports this port's own computed arrays and re-runs the same matmul through the original reference venv's numpy/BLAS build, reproducing the golden fixture with zero differing elements. Everything downstream of the STFT and filterbank-matrix stages (which ARE bit-identical) is verified to within 64 ULPs (worst case observed: 12, on the committed fixtures).

End-to-end acceptance: run against the sibling all-in-one-infer package (3 stems separated once, shared between two isolated environments -- one with real madmom, one with madmom-infer via a thin import madmom compatibility shim) on a 90-second real-music excerpt, harmonix-all model, CUDA: bpm, beats, downbeats, beat_positions, and segments (boundaries + labels) came out byte-for-byte IDENTICAL. The intermediate spectrogram .npy arrays differed by up to ~1778 float32 ULPs (max abs diff ~2.4e-7) on this longer, more complex real track -- the same proven BLAS-non-associativity source as above, confirmed to have zero effect on the final decoded output. A same-environment rerun (real madmom vs. itself) reproduced both the fields and the spectrogram bit-for-bit, confirming the pipeline itself is fully deterministic and isolating the spectrogram delta to the BLAS difference between environments.

Phase 2 -- NN runtime + RNNDownBeatProcessor end-to-end: complete

The weights-bundling question is resolved as: never bundle, always download at runtime (see "What this project will NEVER bundle" below). Phase 2 ships:

  • A forward-pass-only NN runtime (madmom_infer/ml/nn/{__init__,layers, activations}.py), porting exactly the layer/activation types the target ensemble needs: NeuralNetwork/NeuralNetworkEnsemble, FeedForwardLayer, RecurrentLayer, BidirectionalLayer, Gate/Cell/ LSTMLayer, and the linear/tanh/sigmoid/relu/elu/softmax activations. Everything in madmom/ml/nn/* is already forward-inference- only (no backward/train/fit/grad anywhere, confirmed by grep) and pure Python/NumPy (no Cython), so this was a near-mechanical port, same as Phase 1's spectrogram chain.
  • A restricted, class-allowlisted unpickler (madmom_infer/ml/nn/ unpickle.py) for madmom's own .pkl model files -- deliberately NOT a bare pickle.load, since unpickling is inherently code execution and a downloaded model file is a lower-trust artifact than this project's own source. Only the exact class/function paths the target models reference are allowed (see unpickle.py's mapping table); anything else raises loudly.
  • A runtime weights-download layer (madmom_infer/models.py): fetches madmom's DOWNBEATS_BLSTM ensemble (8 downbeats_blstm_[1-8].pkl files) from the official CPJKU/madmom_models GitHub repository over HTTPS, caches them under $XDG_CACHE_HOME/madmom_infer/models/ (never inside this project), and verifies each download's sha256 against a pinned known-good table before use.
  • RNNDownBeatProcessor (madmom_infer/features/downbeats.py): the multi-frame-size (1024/2048/4096) spectrogram + SpectrogramDifference pre-processing cascade, feeding an 8-network BLSTM ensemble, chained into the already-ported DBNDownBeatTrackingProcessor -- audio in, beat/ downbeat times out, matching real madmom exactly (see below).

Verification: unpickled-model structural digest (layer types, shapes, every weight/bias/recurrent/peephole-weight array's sha256, activation function names) matches real madmom's own unpickling exactly, across all 8 ensemble networks (tests/test_ml_nn.py). Activations match to within a documented ULP bound (up to 190 ULP observed, compounding Phase 1's proven BLAS non-associativity across dozens of np.dot calls per ensemble member) -- proven algorithm-exact, not just "close", by the same technique Phase 1 established: this project's own code, re-run under the original reference venv's numpy/BLAS build, reproduces real madmom's recorded activations AND decoded beat/downbeat times with zero differing elements (tests/test_downbeats_rnn.py::test_full_pipeline_is_exact_under_original_blas). Decoded beat/downbeat times are exact in every environment tested (the DBN decode is an integer-domain argmax, which absorbs float32-ULP-scale input noise). Onset/tempo/chord/key/note feature extraction beyond RNNDownBeatProcessor remains out of scope for now (see roadmap below).

Phase 3 (odds and ends)

Remaining audio submodules (chroma, HPSS, cepstrogram) and two more small Cython units: features/beats_crf.pyx and audio/comb_filters.pyx.

What this project will NEVER bundle

madmom's own pretrained model weights (.pkl and similar files) are licensed CC BY-NC-SA 4.0 (non-commercial) by the original authors -- a separate, more restrictive license than madmom's BSD-2-Clause source code. This is a permanent policy, not a phase-2-only caveat: madmom-infer will never bundle, vendor, or redistribute any of madmom's own pretrained weights, in any phase, for any reason. See NOTICE for the full statement.

Instead (Phase 2, madmom_infer/models.py), weights are downloaded at runtime, on demand, directly from the official CPJKU/madmom_models GitHub repository, cached locally under $XDG_CACHE_HOME/madmom_infer/models/ (never inside this project's own package or git history), with sha256 verification against a pinned known-good table. The downloaded weight bytes remain CC BY-NC-SA 4.0 -- non-commercial use only -- regardless of madmom-infer's own BSD-2-Clause license, which covers only this project's source code, never the weights it fetches at runtime. See NOTICE and madmom_infer/models.py's module docstring for the full statement.

Install

pip install madmom-infer

Phase 1 -- the DSP feature-extraction pipeline and numpy Viterbi/DBN downbeat decoder -- and Phase 2 -- the NN runtime, restricted model unpickling, runtime weights download, and RNNDownBeatProcessor end-to-end -- are complete and golden-fixture verified; Phase 3 is not yet started. See the Roadmap above.

Attribution

madmom-infer reimplements algorithms originally published by CPJKU/madmom:

https://github.com/CPJKU/madmom

See LICENSE and NOTICE for full attribution and licensing details.

Development

This project uses uv:

uv sync
uv run python -c "import madmom_infer; print(madmom_infer.__version__)"
uv run pytest -v

The default pytest run above is fully offline (99 tests, network-marked tests deselected by pyproject.toml; this is what CI runs). To also exercise the network-dependent A/B tests against real, freshly-downloaded madmom weights, run uv run pytest -m network -v. See CLAUDE.md's "Phase-2 verification commands" for the full picture, including the reference-venv cross-BLAS proof.

License

BSD-2-Clause. See LICENSE. Note: this covers madmom-infer's source code only -- see "What this project will NEVER bundle" above regarding madmom's separately-licensed pretrained weights.

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

madmom_infer-0.1.0.tar.gz (56.8 kB view details)

Uploaded Source

Built Distribution

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

madmom_infer-0.1.0-py3-none-any.whl (68.5 kB view details)

Uploaded Python 3

File details

Details for the file madmom_infer-0.1.0.tar.gz.

File metadata

  • Download URL: madmom_infer-0.1.0.tar.gz
  • Upload date:
  • Size: 56.8 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for madmom_infer-0.1.0.tar.gz
Algorithm Hash digest
SHA256 117568654cfcb450ea6033f467b38dd2c4036141d7bef1528ebb9df31c744080
MD5 88522426febd759e69c429e1283a1b00
BLAKE2b-256 43dfb752328b3b925d1972ccfc881075a0c889f386b68848c056c4d192360338

See more details on using hashes here.

Provenance

The following attestation bundles were made for madmom_infer-0.1.0.tar.gz:

Publisher: publish.yml on openmirlab/madmom-infer

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file madmom_infer-0.1.0-py3-none-any.whl.

File metadata

  • Download URL: madmom_infer-0.1.0-py3-none-any.whl
  • Upload date:
  • Size: 68.5 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for madmom_infer-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 15d36f8b30ca14d18b88e8f68e46585f218e3a2f3b3ad029ff1cb4de443b8259
MD5 6e5448a0a5c05d3792f821e9333bfbb9
BLAKE2b-256 bbd17ca4b07e55b2b03b92b8d7429a05882571ec8b12fe6e1b12d8b7567093e1

See more details on using hashes here.

Provenance

The following attestation bundles were made for madmom_infer-0.1.0-py3-none-any.whl:

Publisher: publish.yml on openmirlab/madmom-infer

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

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