Skip to main content

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)

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:

  1. Phase shift on slow ballads — the tracker locks onto an offbeat or jumps half a beat partway through.
  2. Doubletime / halftime confusion — pop ballads tracked at 138 BPM that are actually 69 BPM.
  3. 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 final0 checkpoint) 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


Download files

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

Source Distribution

livechord_beat_refiner-0.1.0.tar.gz (33.8 kB view details)

Uploaded Source

Built Distribution

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

livechord_beat_refiner-0.1.0-py3-none-any.whl (30.6 kB view details)

Uploaded Python 3

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

Hashes for livechord_beat_refiner-0.1.0.tar.gz
Algorithm Hash digest
SHA256 6418b22b2143c9994b9c17afb78f7ecfe3c9226811600a2eaedeb40e83ded53c
MD5 8f77d6a0716e18382ab8cbabe33606aa
BLAKE2b-256 59fd2132bf527eca0abfca416958e15cef5220f3f4cf647f6b492d4657c862d3

See more details on using hashes here.

File details

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

File metadata

File hashes

Hashes for livechord_beat_refiner-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 10d5279a01653917a9cab2a69d493f0815d9560846ad9dc3d5f128de8f69dfa2
MD5 7c2d7a73d173f104b8b9835d308ccf2b
BLAKE2b-256 ab7a694f062710bc51000f708a86851e007605d91c2bf3d62b92dd955fad6ddc

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