Downloads · 30 days
103
36% of all-time downloads
litert-community/CREPE-pitch-LiteRT
CREPE-pitch-LiteRT is a audio classification model from litert-community. 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 LiteRT. The card lists the license as mit.
Measured on device (edge-compat): Raspberry Pi 5 · LiteRT 2.2.0.dev20260804 · CPU/XNNPACK, 4 threads · 49.5 ms p50 (2026-08-31); browser · Chromium 151 on M4 Max · LiteRT.js 2.5.3 · WebGPU · 6.04 ms p50 · output match…
Downloads · 30 days
103
36% of all-time downloads
All-time downloads
287
Public
Repo size
44.5 MB
Likes
1
Trending 1
Click a slice to open those files.
.tflite44.5 MB · 100%
From the Hugging Face model README
Measured on device (edge-compat): Raspberry Pi 5 · LiteRT 2.2.0.dev20260804 · CPU/XNNPACK, 4 threads · 49.5 ms p50 (2026-08-31); browser · Chromium 151 on M4 Max · LiteRT.js 2.5.3 · WebGPU · 6.04 ms p50 · output matches CPU (2026-08-11). Record: https://github.com/john-rocky/edge-compat/blob/main/cards/crepe-pitch/CARD.md
CREPE monophonic pitch (f0) estimation, converted to LiteRT and running fully on the CompiledModel GPU (ML Drift) on Android. A 1024-sample (16 kHz) window → activations over 360 pitch bins (20 cents each, ~C1–B7); the host decodes them to a frequency and the nearest musical note. Drives a real-time mic tuner (note + cents flat/sharp).

frame[1,1024] (16 kHz, per-frame zero-mean/unit-var) →[GPU CNN]→ activations[1,360] →[host]→ Hz → note
| nodes on GPU | 49 / 49 LITERT_CL (full residency, single graph, 1 partition) |
| inference | ~75 ms / frame (full model) |
| size | 44.5 MB (fp16) |
| accuracy | fp16 tflite-vs-PyTorch corr 1.000000; self-test (synth 440 Hz) → A4, 440.4 Hz |
The whole network is a pure CNN: 6× {zero-pad → Conv2d → ReLU → BatchNorm → MaxPool} + permute/reshape (≤4D) + Linear + sigmoid. Converted directly with litert-torch — no rewrites:
PAD (not GATHER); no GELU / TransposeConv / dilated conv; the head permute(0,2,1,3).reshape stays ≤4D.op-check: banned NONE, >4D 0; fp16 corr 1.000000.
Mono 16 kHz. Frame into 1024-sample windows; per frame subtract the mean and divide by the std. Decode the 360 activations: peak bin ± 4, activation-weighted average → cents = 20·bin + 1997.3794… → Hz = 10·2^(cents/1200); the peak activation is the confidence. Nearest note: midi = 69 + 12·log2(Hz/440).
Android (Kotlin, CompiledModel GPU)
val model = CompiledModel.create(context.assets, "crepe_full_fp16.tflite",
CompiledModel.Options(Accelerator.GPU), null)
val inputs = model.createInputBuffers(); val outputs = model.createOutputBuffers()
inputs[0].writeFloat(frame) // [1,1024] 16 kHz window, per-frame zero-mean/unit-var
model.run(inputs, outputs)
val act = outputs[0].readFloat() // [360] pitch-bin activations -> host decode to Hz
Python (desktop verification)
import numpy as np, soundfile as sf
from ai_edge_litert.interpreter import Interpreter
wav, _ = sf.read("note_16k.wav", dtype="float32") # mono 16 kHz
f = wav[:1024].copy()
f = (f - f.mean()) / max(f.std(), 1e-10) # per-frame normalize
it = Interpreter(model_path="crepe_full_fp16.tflite"); it.allocate_tensors()
it.set_tensor(it.get_input_details()[0]["index"], f[None]); it.invoke()
act = it.get_tensor(it.get_output_details()[0]["index"])[0] # [360]
c = int(act.argmax()); s, e = max(0, c - 4), min(360, c + 5) # torchcrepe weighted_argmax
w, b = act[s:e], np.arange(s, e)
cents = 20.0 * (w * b).sum() / w.sum() + 1997.3794084376191
hz = 10.0 * 2 ** (cents / 1200.0)
midi = 69 + 12 * np.log2(hz / 440.0)
names = ["C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "B"]
n = int(round(midi))
print(f"{hz:.1f} Hz {names[n % 12]}{n // 12 - 1} {round((midi - n) * 100):+d} cents conf {act[c]:.2f}")
| File | What |
|---|---|
crepe_full_fp16.tflite | the full CREPE model, fp16, frame [1,1024] → activations [1,360] |
build_crepe.py | conversion + parity + 220/440/880 Hz self-test |
Measured on a Pixel 8a (Tensor G3, Android 16) with the standard TFLite benchmark_model tool — 10 warm-up runs then 50 timed runs, reported as the tool's mean.
| Runtime | Backend | Graph on GPU | Latency |
|---|---|---|---|
LiteRT CompiledModel (LITERT_CL) | GPU | 49 / 49 | ~75 ms |
TFLite benchmark_model (TfLiteGpuDelegateV2) | GPU (OpenCL) | 49 / 49 | 81.0 ms |
TFLite benchmark_model | CPU (XNNPACK, 4 threads) | — | XNNPACK declined the graph |
The two GPU rows are different runtimes, not a contradiction. The LITERT_CL figure is the one recorded when this model shipped, taken through LiteRT's own CompiledModel accelerator — the path the Kotlin sample app and the LiteRT API use. The TfLiteGpuDelegateV2 figure is the classic TFLite OpenCL delegate, measured with a tool anyone can download and re-run. They agree on how much of the graph the GPU takes; they disagree on speed, and the classic delegate is the slower of the two here. Read the TfLiteGpuDelegateV2 row as a reproducible floor, not as this model's speed on LiteRT.
XNNPACK declines these fp16 graphs — it reports failed to delegate DEPTHWISE_CONV_2D and then fails to allocate tensors — so there is no usable CPU number. Disabling XNNPACK falls back to reference kernels, which measured about 20× slower than the GPU on models of this size and would not represent CPU inference anyone would ship.
The NPU compiles this graph and then fails to run it: LiteRtException: Failed to invoke the compiled model. The GPU row below is the only S26 figure for it. A clean compile is not evidence that a model runs.
| backend | inference (median / min) | load |
|---|---|---|
| GPU (Adreno) | 8.84 ms / 8.53 ms | 445 ms |
Measured on a Samsung Galaxy S26 (Snapdragon 8 Elite Gen 5 / SM8850, Hexagon v81, Android 16), LiteRT CompiledModel 2.2.0, one accelerator per process, 5 warm-up runs then N=50 timed runs, median reported. The run held thermal status NONE throughout. Headroom 0.62, where 1.0 is the throttling threshold.
Recipes for both accelerators: NPU · GPU.
Measured on a Raspberry Pi 5 Model B Rev 1.1 (8 GB, Raspberry Pi OS 64-bit) with the LiteRT benchmark_model tool from litert-cli-nightly 0.2.0.dev20260805: CPU inference (XNNPACK, 4 threads), 3 invocations per file of 10 warm-up plus 50 timed runs (the tool caps a phase at 150 s, so very slow graphs run fewer — the Runs column is the actual timed total). The latency is the median across invocations; the spread is the min–max over all timed runs. No thermal throttling occurred during these runs (vcgencmd get_throttled stayed 0x0).
| File | Inference (median) | Spread (min–max) | Runs | Peak memory |
|---|---|---|---|---|
crepe_full_fp16.tflite | 49.5 ms | 41.0–66.1 ms | 150 | 191 MB |
MIT. Upstream: marl/crepe (Kim, Salamon, Li, Bello — "CREPE: A Convolutional Representation for Pitch Estimation", ICASSP 2018); PyTorch weights via torchcrepe (MIT).
@inproceedings{kim2018crepe,
title={CREPE: A Convolutional Representation for Pitch Estimation},
author={Kim, Jong Wook and Salamon, Justin and Li, Peter and Bello, Juan Pablo},
booktitle={IEEE International Conference on Acoustics, Speech and Signal Processing (ICASSP)},
year={2018}
}