Recently I’ve been building StemLab, a local music-analysis toolkit for getting a clearer picture of what is happening inside a song.
It grew out of a practical problem. I wanted better evidence for timing visual effects and choosing sections for Shorts, without relying on one BPM estimate or treating the output of a single model as the answer. A song can be rhythmically steady but shift phase, a section label can be plausible but wrong, and a cut that looks neat on a timeline can still click or clip a vocal when it repeats.
StemLab brings several kinds of evidence together on the same clock. Depending on the selected analysis profile, it can produce separated stems, beat and downbeat grids, tempo-regime analysis, song structure, harmony, key and tuning evidence, melody and note data, loudness and dynamics, timbre, stereo information, speech and lyric timing, and semantic summaries.
It also finds candidate loops for verses and choruses. These are aligned to complete bars and retain exact source-sample boundaries. StemLab checks the beat grid, available vocal evidence and the waveform on every channel around the join. If a section does not have enough trustworthy evidence, it is left unresolved rather than being forced into a confident-looking result. Listening is still part of the final check.
Keeping the evidence inspectable
The most important part for me is not simply producing a large folder of model output. StemLab records the source hash, the tools and settings used, backend errors and hashes for its generated artefacts. Numerical results are available in JSON, TSV and NPZ files, and it can create a portable Sonic Visualiser session.
There is also a browser workspace built around the separate react-timeline-sequence control. It places playback, waveforms, spectrograms, beats, sections, lyrics and loop candidates under one shared playhead, so the relationships are easier to inspect instead of being scattered across unrelated reports.

Where it fits
StemLab is the music-understanding part of my wider DanceFlow work. Its timing and analysis artefacts can feed motion-aware projects such as the WordPress DanceMoves plugin, but StemLab is not tied to WordPress and is useful on its own.
It can be used as a command-line tool, Python library, local web service, container or local Codex MCP plugin. The public Python distribution is called danceflow-stemlab because package-registry names are globally unique; the project, Python import and command are all still called StemLab.
The full analysis can be deliberately demanding, particularly when the large separation models are enabled, so there is a lighter practical profile for iteration. Optional models also retain their own licences and runtime requirements. StemLab’s source is MIT licensed, but that does not silently change the terms attached to third-party model weights.
Getting BeatNet working on current Python
One practical snag was BeatNet. Its current PyPI release pins an old combination of Numba and NumPy that cannot be resolved alongside the maintained madmom-prebuilt package. The model code itself still works on current Python once that old package metadata is kept separate from the runtime.
In my current development checkout, StemLab now treats BeatNet as a pinned external runtime. It downloads the official BeatNet 1.1.3 wheel, checks the SHA-256 published by PyPI, and exposes only the code and bundled weights from its cache. Offline DBN decoding uses madmom-prebuilt. If the runtime is missing or the hash is wrong, StemLab reports it as unavailable rather than pretending that detector ran.
I tested the real model on CPython 3.13.14 with a deterministic 16-second click track at 120 BPM. BeatNet returned 120.0 BPM, 32 beats and 16 downbeats, and StemLab wrote both its JSON and TSV results. That proves the installation, inference and output path; it does not prove that BeatNet will be right about every song.
This is still worth fixing upstream. BeatNet’s open installation issue records the released Numba-pin problem, while the current source has already relaxed the worst of those old pins. A focused pull request could finish the job by using a modern offline decoder dependency, keeping PyAudio optional, and testing Python 3.11 to 3.13 before a fresh release. Until that happens, StemLab’s verified bootstrap keeps the workaround narrow and reproducible.
What changed in StemLab 1.2
StemLab 1.2 adds several ways of getting more specific evidence without rerunning the whole analysis. It can now compare an LRC cue sheet with an existing beat grid and produce a separate review copy. Nearby timestamps can be snapped precisely, while distant or forced changes stay flagged rather than quietly becoming the new truth.
There is also a new audio-to-MIDI layer. It includes Basic Pitch for isolated pitched parts, specialist piano routes, and multi-instrument options. Each model keeps its own MIDI and normalised note data, but the result is still an inference from audio. A model’s tempo events do not replace StemLab’s measured beat grid.
A separate evidence layer can add vocal-activity, forced lyric-alignment, pitch, song-structure, harmony and drum hypotheses. The useful part is that these results stay independent: they can support or challenge another result without rewriting approved lyrics, section names or timing. Some research models need their own Python environment or separately licensed weights, so StemLab lists what is available without pretending that every model is ready to run.
StemLab 1.2.0 is now available from the GitHub repository. The README contains the installation options, analysis detail, examples and current limitations. You can also browse the rest of my music work in My Songs.



Leave a Reply