Downloads · 30 days
0
rodriler/betterchord-chord-cnn
betterchord-chord-cnn is a audio classification model from rodriler. Use it for the audio classification task on the model card, and read the license before you ship it in a product. It is set up for pytorch. The card lists the license as cc-by-nc-4.0.
All inference code can be found in the BetterChord repo.
Downloads · 30 days
0
Access
Public
Updated Sep 21, 2026
Repo size
240 MB
Likes
1
Trending 1
Click a slice to open those files.
.pth120 MB · 50%
From the Hugging Face model README
All inference code can be found in the BetterChord repo.
chord_cnn.pth is a small PyTorch CNN that identifies the chord in a
single recorded guitar strum. It takes an 84x119 CQT spectrogram of ~2.75
seconds of audio and outputs three 12-way predictions over the chromatic
scale: which notes are sounding (multi-label), which note is the root, and
which note is the bass. It does not output a chord name directly --
those three raw predictions are turned into a name (e.g. Cmaj7, A6/9,
D/F#) by a separate rule-based module downstream, so the model itself
isn't limited to a fixed list of chord types. Weights also ship as
chord_cnn.onnx (same weights, converted for lightweight inference
without PyTorch).
import torch
from cnn_model import ChordCNN # betterchord/training_scripts/
from audio_processing import load_audio, create_spectrogram
from music_theory import identify_chord_smart # betterchord/config/
model = ChordCNN()
model.load_state_dict(torch.load("chord_cnn.pth", map_location="cpu"))
model.eval()
y, sr = load_audio("strum.wav")
spec = create_spectrogram(y, sr) # (84, 119)
x = torch.from_numpy(spec).float()[None, None, :, :] # (1, 1, 84, 119)
with torch.no_grad():
note_logits, root_logits, bass_logits = model(x)
result = identify_chord_smart(
note_logits[0].numpy(), root_logits[0].numpy(), bass_logits[0].numpy()
)
print(result["chord"]) # e.g. "Cmaj7"
identify_chord_smart expects raw logits and handles the sigmoid/softmax,
root/bass reconciliation, and chord naming itself. The .onnx file can be
run the same way through onnxruntime.InferenceSession instead (input
name "input", outputs "note_out" / "root_out" / "bass_out") -- see
export_onnx.py in the repo for that path.
Semitone-shift (-2 / +2) pitch-transposition augmentation was applied during dataset preparation where the source audio allowed it (Ie, E major with voicing 0-2-2-1-0-0 was shifted up to F, with one semitone, and F#, with two semitones).
This model is built to identify the chord in a single, isolated, reasonably clean guitar strum (electric or acoustic, standard tuning, root-position or common voicings) -- roughly a 1-3 second clip, which is exactly how the BetterChord app uses it: record or upload a strum, get a chord name back. It is not built for full-song chord transcription over time, chord sequences, multi-instrument mixes, non-guitar sources, alternate tunings, or melodic/single-note input -- it consumes one fixed window and returns one prediction.
A small CNN (~30M parameters) with a shared convolutional trunk and three parallel output heads (note / root / bass), trained end-to-end on roughly 50,000 samples across ~696 root-position chord classes. See the GitHub repo for the full architecture, preprocessing constants, and training hyperparameters.
Headline eval numbers (project-recorded, not re-measured for this card):
~92.96% test-set accuracy, 98%+ rule-based root accuracy once
identify_chord_smart's reconciliation is applied, and 16-17 of 19 on
a small held-out set of real-world recordings.
The model is noticeably less reliable on noisy recordings. A strum with significant background noise can degrade detection meaningfully, so a clean-ish take (or a quiet-ish room) gets much better results than a noisy one.
Beyond noise, three patterns show up consistently in my own testing: