--- license: cc-by-4.0 library_name: gguf tags: - guitar - tablature - music-information-retrieval - audio-to-tab - crispasr - gguf pipeline_tag: audio-classification --- # TabCNN — guitar tablature emission scorer (GGUF) GGUF conversion of **TabCNN** for [CrispASR](https://github.com/CrispStrobe/CrispASR)'s `--tab` surface. 833,982 parameters. Per frame, the model emits **six independent distributions over 21 fret classes** — one per guitar string. It contains no decoder: no inter-string coupling, no temporal model, no search. > ⚠️ **These are emission SCORES, not a decided tablature.** Turning them into a > playable fingering needs a constrained decoder (one note per string, fret > range, capo, hand span). Taking the argmax of this grid ignores every > playability constraint. CrispASR ships the scorer and leaves the decoder to > the caller. ## Provenance and licence — verifiable, not asserted **Source artifact** (fetch it and check the hashes yourself): | | | |---|---| | file | `best_TabCNN_tablature_trancription_model` (sic — typo is upstream's) | | direct URL | | | record | | | size | 3,345,122 bytes | | md5 (upstream, from the Zenodo API) | `ce168b2cd426f81a2a78499214e40605` | | sha256 (computed on the bytes converted) | `1470a308896629352a811082843eb708cbc2f1aa3092757340055ef76a53ed0c` | Every GGUF here carries these as metadata (`general.source.url`, `general.source.record_url`, `general.source.md5_upstream`, `general.source.sha256`), so provenance travels with the artifact. ⚠️ **There is no Zenodo DOI for this record.** `10.5281/zenodo.11406378` looks plausible and **404s** — it does not exist. The record's actual DOI is the arXiv one, `10.48550/arXiv.2405.14679` (resolves). Cite what resolves. **Licence evidence.** The Zenodo API reports, for record 11406378: ```json "metadata": { "license": {"id": "cc-by-4.0"}, "access_right": "open" } ``` Zenodo has no per-file licensing (a file object exposes only `checksum`, `id`, `key`, `links`, `size`), so the record licence governs every deposited file. And the record description states explicitly that the weights are part of the deposit: > "The weights for the best performing model (TabCNN trained with > \"GuitarProFX\") in the paper are also provided." That sentence is also what confirms this is the **GuitarProFX-augmented** variant rather than the baseline — the distinction that matters, since the baseline collapses from tablature F1 0.748 to 0.447 on real electric guitar while the augmented one recovers to 0.585 (DAFx-24). **Required citation** (the record asks for this explicitly): > Pedroza HE, Abreu W, Corey R, Roman IR. "Leveraging real electric guitar tones > and effects to improve robustness in guitar tablature transcription modeling." > In *27th International Conference on Digital Audio Effects (DAFx)*, 2024. **Upstream chain:** | | | |---|---| | **Model** | TabCNN — Wiggins & Kim, *Guitar Tablature Estimation with a Convolutional Neural Network*, ISMIR 2019 | | **Reference implementation** | [`amt-tools`](https://github.com/cwitkowitz/amt-tools) (Cwitkowitz) — MIT | | **Training corpus** | [GuitarSet](https://zenodo.org/records/3371780) — CC BY 4.0 | ## Files | file | size | tablature F1 | vs f32 | notes | |---|---|---|---|---| | `tabcnn-f16.gguf` | 1.78 MB | 0.7732 | **0.0000** | **default** — lossless, 100 % argmax agreement | | `tabcnn-q8_0.gguf` | 1.10 MB | 0.7749 | +0.0017 | `dense0` q8_0, `head` f32 | | `tabcnn-q4_k.gguf` | 0.72 MB | 0.7749 | +0.0017 | `dense0` Q4_0, `head` f32 — smallest, no measurable loss | | `tabcnn-f32.gguf` | 3.34 MB | 0.7732 | — | full precision, for parity work | (F1 on EGSet12 track 01 against its JAMS ground truth; the +0.0017 is noise on a single 10 s clip — read it as "no loss", not "better".) ### The output layer must stay at full precision Only **two** tensors are quantizable at all: the conv stack is 3×3, so `ne0=3`, far below any block size, and the biases are 32–126 wide. `dense0.weight` (761 k of 834 k params) is effectively the whole model. Quantizing `dense0` costs nothing — but quantizing `head.weight` alongside it is catastrophic: | | head quantized | head preserved | |---|---|---| | q8_0 | 0.7676 (−0.0057) | 0.7749 | | Q4_0 | **0.7153 (−0.0579)** | 0.7749 | `head.weight` is 16 k params, 1.6 % of the file, and directly determines the 21-way per-string softmax. Preserving it makes Q4_0 as accurate as q8_0. `crispasr-quantize` encodes this as an arch rule; anyone converting these weights by another route should do the same. ⚠️ **`q4_k` here is really Q4_0.** No tensor has `ne0 % 256 == 0` (`dense0.weight` is 5952; 5952 % 256 = 64), so k-quants cannot apply and the quantizer falls back to Q4_0. The filename keeps the requested name; the content is Q4_0. ## Front end — read it from the file, do not assume it ``` CQT: sr 22050, hop 512, 192 bins, 24 per octave, fmin C1 (32.70 Hz) -> amplitude_to_db(ref = max of the WHOLE clip) -> [-80, 0] -> /80 + 1 -> [0, 1] -> 9-frame centred context window ``` ⚠️ **`fmin` is C1, not the guitar's low E.** Assuming E2 is the obvious guess and it is wrong — and every wrong value still *runs*, producing plausible tensors that pass shape and cosine checks while the model emits garbage. Measured on EGSet12 track 01: fmin C1 → tablature **F1 0.771**, E1 → 0.040, E2 at 44.1 kHz → **0.001**. All of these constants are stored as GGUF metadata (`tabcnn.sample_rate`, `tabcnn.fmin_hz`, …) precisely so a consumer never has to guess. ⚠️ `ref = max of the whole clip` is a **per-clip normalisation**, so features cannot be computed streaming or chunked without changing them. This model is two-pass by construction. ## Usage ```bash crispasr --tab -m tabcnn-f16.gguf -f guitar.wav crispasr --tab -m tabcnn-f16.gguf -f guitar.wav --tab-format json ``` For real use, take the log-probabilities through the C ABI (`crispasr_session_tab` → `crispasr_session_tab_emissions`) and run your own constrained decoder. `crispasr_session_tab_silent_class()` tells you which class means "not played" — read it rather than assuming it is the highest index. ## Validation `crispasr-diff tabcnn` against a reference dump from the `amt-tools` model, run from the **waveform** (not replayed features, so the CQT is covered): | stage | cos | |---|---| | `cqt_db` | 0.998925 | | `conv0_relu` | 0.991401 | | `conv1_relu` | 0.987366 | | `conv2_relu` | 0.972109 | | `pool` | 0.975496 | | `dense0_relu` | 0.991824 | | `logits` | 0.999661 | End to end on EGSet12 track 01 against its JAMS ground truth: | | P | R | F1 | |---|---|---|---| | torch reference | 0.7183 | 0.8315 | 0.7708 | | CrispASR (this GGUF) | 0.7179 | 0.8378 | **0.7732** | ΔF1 **+0.0024**, argmax agreement **98.57 %**. The residual difference is the front end — CrispASR uses direct Brown-kernel CQT against librosa's recursive downsampling — and it costs nothing on the task metric. ## Limitations - Trained on solo guitar. Mixed music is out of domain. - GuitarSet numbers overstate real-world performance; EGSet12 is the honest reference point. - The scorer is per-frame and per-string independent, so it can emit physically impossible combinations. That is expected — the decoder is where playability is enforced.