Bidirectional Transformer beat / downbeat / chord-boundary refiner — denoises beat_this, madmom, or librosa output using full audio context.
Project description
license: apache-2.0 library_name: pytorch tags:
- music
- music-information-retrieval
- beat-tracking
- downbeat-tracking
- chord-recognition
- audio
- transformer pipeline_tag: audio-classification model-index:
- name: livechord-beat-refiner
results:
- task:
type: beat-tracking
name: Beat tracking refinement
metrics:
- type: f1 value: 0.920 name: Beat F1 (gold-quality holdout, ±93ms)
- type: f1 value: 0.936 name: Downbeat F1 (gold-quality holdout, ±93ms)
- type: f1 value: 0.881 name: Beat F1 (full holdout, ±93ms)
- type: f1 value: 0.914 name: Downbeat F1 (full holdout, ±93ms)
- task:
type: beat-tracking
name: Beat tracking refinement
metrics:
livechord-beat-refiner
Bidirectional Transformer that refines a prior beat tracker's output (beat_this, madmom, or librosa) using full-context audio features. Drops in as a post-processing pass to fix phase drift and bar misalignment without re-running the expensive front-end model.
🎹 Try it live
This model powers the beat / bar grid behind the chord ribbon, 88-key waterfall, and AI accompaniment at livechord.org — upload an MP3 and play along on a virtual piano with chord cards that follow the music.
Quickstart
pip install livechord-beat-refiner
from livechord_beat_refiner import refine
# 1. Run any beat tracker (beat_this, madmom, librosa, ...) → beats / downbeats lists
# 2. Pass them to refine() together with the audio file:
out = refine(
audio_path="song.flac",
beats=[0.0, 0.52, 1.05, 1.57, ...], # seconds
downbeats=[0.0, 2.10, 4.21, ...], # seconds
)
print(out["refined_beats"]) # list[float] — refined beat times
print(out["refined_downbeats"]) # list[float]
print(out["applied"]) # bool — False if model couldn't run; input echoed back
The checkpoint is downloaded from this Hub repo on first call (cached
under ~/.cache/huggingface/hub). To use a local file, pass
checkpoint_path="path/to/model.safetensors".
What the model does
audio (22050 Hz, mono)
│
▼
[ CQT 84 + chroma 12 + onset + RMS ] ← 98 audio channels
[ initial beat grid ] ← 1 channel (from prior tracker)
[ initial downbeat grid ] ← 1 channel
│ concat → (T, 100)
▼
[ Linear(100→256) + sinusoidal pos enc ]
│
▼
[ TransformerEncoder × 6 (d=256, h=4, ffn=512, GELU, pre-LN) ]
│
▼ ┌────────────┐
├──────────────┤ beat head ├──→ sigmoid → peak-pick → beat times (s)
│ └────────────┘
│ ┌────────────┐
├──────────────┤ db head ├──→ sigmoid → peak-pick → downbeat times (s)
│ └────────────┘
│ ┌────────────┐
└──────────────┤ cb head ├──→ aux supervision (not production-ready, see Limitations)
└────────────┘
- Parameters: ≈ 3.0 M
- Frame rate: 10.766 fps (sr=22050, hop=2048)
- Max audio length: ≈ 15 min (longer truncated)
- Inference budget: ~1.8 s per minute of audio on CPU; ~0.3 s/min on a recent NVIDIA GPU.
Why a refiner
State-of-the-art beat trackers (beat_this, madmom) are strong on percussive genres but drift in three failure modes that show up over and over in real-world libraries:
- Phase shift on slow ballads — the tracker locks onto an offbeat or jumps half a beat partway through.
- Doubletime / halftime confusion — pop ballads tracked at 138 BPM that are actually 69 BPM.
- Bar misalignment — beats are right, downbeats off by one.
Re-running the front-end tracker rarely helps (same audio, same answer). The refiner sees both the audio AND the prior tracker's grid as input hints, then re-emits a cleaner grid by attending across the full song with chord-boundary cues as auxiliary supervision.
Metrics
Held-out test set: 7,389 songs (15% stratified holdout from a 13,017-song corpus). Tolerance: ±1 frame ≈ ±93 ms (mir_eval-style F-measure; standard is 70 ms).
Headline (gold-quality subset, n=510)
Songs whose prior-tracker output passed coefficient-of-variation < 0.05 AND
chord-change alignment ≥ 0.5 filters — i.e. the most reliable references.
| Metric | F1 | Precision | Recall |
|---|---|---|---|
| Beat | 0.920 | 0.910 | 0.933 |
| Downbeat | 0.936 | 0.925 | 0.952 |
Full holdout (n=7,389)
| Metric | F1 | Precision | Recall |
|---|---|---|---|
| Beat | 0.881 | 0.859 | 0.914 |
| Downbeat | 0.914 | 0.906 | 0.930 |
The "skip" quality bucket (6,087 / 7,389 songs) consists of tracks whose prior-tracker reference didn't pass the gold/ok filters, so their "labels" are noisy by construction. The lower full-holdout F1 reflects label noise, not model regression — quality is monotonic (gold > ok > skip), which is the structural sanity check.
Per-bucket
| beat_quality | n | beat F1 | downbeat F1 |
|---|---|---|---|
| gold | 510 | 0.920 | 0.936 |
| ok | 792 | 0.906 | 0.925 |
| skip | 6,087 | 0.874 | 0.910 |
Reproduce with the eval script in the LiveChord repo.
Note on baseline
The training corpus uses each song's own prior-tracker output as the supervision target whenever that output passed the filter. So the F1 reported here measures how well the refiner preserves correct beats while also absorbing chord-boundary auxiliary signal — not "refiner > beat_this" head to head. An independent ground-truth comparison (MIREX Beatles, GTZAN_rhythm) is on the v2 roadmap.
Training data
- Corpus: 13,017 songs from a personal music library, predominantly East Asian pop, Western pop / rock / R&B, jazz standards, and a smaller classical / folk tail.
- Labels: prior-tracker output (beat_this
final0checkpoint) filtered to gold / ok quality bands by coefficient-of-variation and chord-alignment heuristics. - Split: 70% train / 15% val / 15% test, stratified by (beat_quality × chord_quality).
- Augmentation: random initial-grid corruption during training (jitter / drop / insert / phase-shift) so the model learns to denoise rather than copy the input grid verbatim.
Limitations
- Chord-boundary head is auxiliary supervision, not a production target. cb F1 on gold = 0.243. v1 didn't tune class weights or peak-picking thresholds for this head; treat the cb output as exploratory.
- No genre rebalancing. Folk / classical / EDM are underrepresented vs pop; performance there is more variable.
- Long songs (> 15 min) are truncated. v1 has no chunked-overlap inference. If you need this, please file a GitHub issue.
- Label noise. Because labels are filtered prior-tracker output, the model inherits any systematic biases that survived the filter.
How to use with beat_this
beat_this (CPJKU 2024) is a strong
front-end choice, especially for percussive genres. Pipeline:
from beat_this.inference import File2Beats
from livechord_beat_refiner import refine
# Front-end (GPU-friendly, run once per song)
predictor = File2Beats(checkpoint_path="final0", device="cuda", float16=True)
init_beats, init_downbeats = predictor("song.flac")
# Refine
out = refine(
audio_path="song.flac",
beats=init_beats,
downbeats=init_downbeats,
)
final_beats, final_downbeats = out["refined_beats"], out["refined_downbeats"]
Citation
@misc{livechord-beat-refiner,
title = {livechord-beat-refiner: a bidirectional Transformer for
beat / downbeat / chord-boundary refinement},
author = {LiveChord Project},
year = {2026},
url = {https://huggingface.co/livechord-music/livechord-beat-refiner},
note = {Refines beat\_this / madmom / librosa output using full
audio context. Trained on 13,017 songs.},
}
License
Apache License 2.0 — code AND weights. The training data is not redistributed; only the trained model artifact is released.
The LiveChord product (full FastAPI server, frontend, and AI pipeline) is released separately under AGPL v3 at github.com/JJ110112/LiveChord. This package is the standalone inference release.
Related
- livechord-bar-arbitrator —
companion phase-correction post-processor that runs after this model to
fix bar / beats-per-bar / doubletime confusion using
chords[]as an additional signal. - CPJKU/beat_this — recommended upstream beat tracker.
- livechord.org — the live application this model powers.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file livechord_beat_refiner-0.1.0.tar.gz.
File metadata
- Download URL: livechord_beat_refiner-0.1.0.tar.gz
- Upload date:
- Size: 33.8 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.11.9
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
6418b22b2143c9994b9c17afb78f7ecfe3c9226811600a2eaedeb40e83ded53c
|
|
| MD5 |
8f77d6a0716e18382ab8cbabe33606aa
|
|
| BLAKE2b-256 |
59fd2132bf527eca0abfca416958e15cef5220f3f4cf647f6b492d4657c862d3
|
File details
Details for the file livechord_beat_refiner-0.1.0-py3-none-any.whl.
File metadata
- Download URL: livechord_beat_refiner-0.1.0-py3-none-any.whl
- Upload date:
- Size: 30.6 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.11.9
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
10d5279a01653917a9cab2a69d493f0815d9560846ad9dc3d5f128de8f69dfa2
|
|
| MD5 |
7c2d7a73d173f104b8b9835d308ccf2b
|
|
| BLAKE2b-256 |
ab7a694f062710bc51000f708a86851e007605d91c2bf3d62b92dd955fad6ddc
|