alberto chord recognizer (CRNN, major/minor triads)
A small convolutional-recurrent network that labels guitar audio with one of 25 classes per frame: no chord, or a major or minor triad on any of the 12 roots. It is the neural recognizer of alberto, a guitar songwriting helper that listens to a progression, estimates the key, and suggests what to play next.
- Parameters: 475,833 (1.9 MB checkpoint)
- Input: log-magnitude constant-Q transform, C2 upward, 6 octaves, 24 bins per octave,
hop 512 at 22.05 kHz (
config.jsonhas every value) - Output: per-frame logits over 25 classes (
config.jsonlists them in order) - Runs comfortably on CPU: ~5 ms for an 8-second window on an M-series Mac
Training
Trained on GuitarSet (Xi, Bittner, Ye, Bello, ISMIR 2018; CC BY 4.0), mono-mic recordings, using the performed chord annotation reduced to major/minor by the chord's third. Players 00–04 (300 excerpts, strummed and solo) were used for training with random pitch shifts of up to ±5 semitones applied by rolling CQT bins; player 05 (60 excerpts) was held out. 30 epochs, Adam 1e-3 with cosine decay, 8-second chunks, cross-entropy.
Evaluation (mir_eval, held-out player 05, 30 strummed excerpts)
| metric | this model | binary-template baseline |
|---|---|---|
| root | 0.740 | 0.541 |
| majmin | 0.684 | 0.516 |
| thirds | 0.700 | 0.479 |
| mirex | 0.593 | 0.506 |
Scored against the performed annotation. sevenths and triads are capped by the
major/minor output space. On single-note solo excerpts (not the intended use) majmin is
0.271.
Usage
The model definition lives in the alberto repository (alberto/chords/model.py). With it
on your path:
from huggingface_hub import hf_hub_download
from alberto.chords.model import recognize
weights = hf_hub_download("shawon/alberto-chord-crnn", "crnn_majmin.pt")
segments = recognize(y, sr, weights=weights) # y: mono float32 numpy array
for seg in segments:
print(f"{seg.start:6.2f} {seg.end:6.2f} {seg.chord}")
Or load the raw checkpoint into your own copy of the architecture:
import torch
ckpt = torch.load("crnn_majmin.pt", map_location="cpu", weights_only=True)
model = ChordCRNN(n_bins=ckpt["n_bins"]).eval()
model.load_state_dict(ckpt["state_dict"])
logits = model(torch.from_numpy(features).unsqueeze(0)) # (1, n_frames, 25)
Frames whose RMS is below -60 dBFS should be treated as no-chord regardless of the prediction; alberto also applies a 5-frame mode filter before merging frames into segments.
Limitations
- Major and minor triads only; sevenths, suspended and diminished chords map to the nearest triad.
- Trained on one acoustic guitar recorded with one microphone. Expect lower accuracy on electric guitar, distortion, or full mixes.
- Assumes the root is the lowest note, as in ordinary guitar voicings.
- Downloads last month
- 15