Pitchy

A pitch (f0) tracker for the audio speech systems actually get: phone lines, noisy rooms, reverberant microphones. One ONNX graph, 16 kHz audio in, a pitch salience every 10 ms out, on one CPU thread; the PyTorch model it was exported from is here too.

Work in progress. Pitchy is still being trained, and this repository is updated as better models pass our development-set rules; every upload is its own revision, so pin one. In progress now: clean studio speech, where FCNF0++ (trained on PTDB-TUG's speakers) still leads on PTDB-TUG by 0.33 points of frame error; pitch precision, where the lead is in voicing rather than pitch (86.6 % of voiced frames within 50 cents over every condition, RMVPE 87.3 %); and music and pitch above 800 Hz, which the training speech does not reach (MDB-stem-synth: 27.2 % frame error, SwiftF0 13.0 %). The numbers below are the model in this revision.

Pitchy against the best published tracker per condition family

Revisions

  • w7_rv_hold10 (2026-10-28): voices held vowels and codec-decoded TTS speech. The first upload called a held vowel unvoiced (music under its training speech had taught it that a held harmonic sound is not a voice) and dropped voicing for up to 1.84 s of loud TTS speech; this one also learns voicing from the WORLD resyntheses, a tenth of them with one vowel held 0.5 to 2 s. On a TTS stability study's 69 passages, the longest stretch of loud speech that RMVPE, FCPE and CREPE all call voiced and Pitchy does not is 4 frames at most (the first upload: 184); a held 1.5 s vowel is 100 % voiced (was 52 %); voicing holds through a sudden +3 semitone step on 99 % of frames (was 79 %). It gives a little back on natural speech: frame error over every condition 4.17 % on PTDB-TUG's test speakers (the first upload 4.02 %) and 8.79 % on Keele (7.57 %), where it misses more voiced frames in heavy noise; on music it is much better (27.2 % on MDB-stem-synth, was 54.1 %). The first upload stays available as revision first-release.
  • w4_mel_rmvpev_norv (2026-10-27): the first upload.

Use

ONNX, with numpy and onnxruntime only (pitchy_onnx.py):

from pitchy_onnx import Pitchy
m = Pitchy('pitchy.onnx')
f0, voiced, confidence = m(audio_16k)

PyTorch, with torch, numpy and safetensors (pitchy_torch.py, the network code; pitchy.safetensors, the weights):

import torch, pitchy_torch
m = pitchy_torch.load('.')                 # config.json + pitchy.safetensors
salience, voicing = m(torch.from_numpy(audio_16k)[None])

Contract: audio [1, N] float32 at 16 kHz -> salience [1, T, 360] (CREPE's grid, 20 cents from 32.70 Hz; its peak is the voicing probability) and voicing [1, T]; T = N // 160 + 1, frame i centred at i x 10 ms. Voiced where the peak exceeds 0.869, the threshold chosen on PTDB-TUG's development speakers.

Benchmark

F0 frame error (FFE, the headline), gross pitch error (GPE) and voicing decision error (VDE), % and lower is better; every threshold and frame lag chosen on PTDB-TUG's development speakers only. The best published tracker is the lowest FFE of 14 trackers, each with its better decoder, per row. Four of them were trained on PTDB-TUG; Keele and FDA are free of that.

Set Conditions Pitchy FFE GPE VDE Best published FFE GPE VDE
PTDB-TUG test speakers Clean 3.37 1.38 3.06 FCNF0++ 3.04 1.49 2.70
PTDB-TUG test speakers Real noise, 5 and 0 dB 4.80 1.88 4.40 RMVPE 12.78 1.51 12.49
PTDB-TUG test speakers Real rooms 4.66 2.08 4.22 RMVPE 8.92 1.33 8.69
PTDB-TUG test speakers Phone line (8 kHz codecs) 4.13 1.63 3.77 RAPT 6.98 7.45 5.38
PTDB-TUG test speakers Room, noise and line 5.05 2.27 4.58 SwiftF0 14.26 2.07 13.99
PTDB-TUG test speakers Every condition 4.17 1.72 3.80 RMVPE 9.61 1.47 9.34
Keele Clean 5.33 0.44 5.12 RAPT 5.04 2.12 3.98
Keele Real noise, 5 and 0 dB 12.36 0.82 12.03 RMVPE 16.50 1.22 16.02
Keele Real rooms 11.54 1.22 11.03 RMVPE 21.02 1.02 20.66
Keele Phone line (8 kHz codecs) 7.03 1.04 6.54 YAAPT 8.99 3.88 7.18
Keele Room, noise and line 11.80 1.21 11.29 YAAPT 21.44 8.74 17.41
Keele Every condition 8.79 0.76 8.46 RMVPE 14.86 0.90 14.51
FDA Clean 4.02 0.40 3.88 RMVPE 4.25 0.21 4.17
FDA Real noise, 5 and 0 dB 6.47 1.17 6.08 RMVPE 11.72 0.66 11.52
FDA Real rooms 6.26 0.92 5.96 RMVPE 13.70 0.68 13.52
FDA Phone line (8 kHz codecs) 4.65 0.66 4.42 RAPT 6.74 3.85 5.45
FDA Room, noise and line 6.56 1.03 6.23 RMVPE 16.84 0.85 16.64
FDA Every condition 5.20 0.72 4.95 RMVPE 9.32 0.42 9.20

Every tracker on PTDB-TUG's test speakers by condition family (log scale; each row sorted by its error over every condition):

Pitchy and every published tracker by condition family

Cost

3.78M parameters, 19.6 MB, 0.010 s of one CPU thread per second of audio, peak memory 239 MB on 30 s. The graph matches PyTorch to 0.0002 cents on speech and 0.0102 cents on a 30 s pure tone, with every voicing decision equal (our budget is 0.01 cents: this revision misses it on the tone by 0.0002); the standalone PyTorch module here matches the graph to 0.0102 cents (100 % voicing agreement).

Cost against error

Training data and licences

LibriTTS-R train-clean-100 (CC BY 4.0). Half the training audio is that speech, with pitch targets where RMVPE (RVC weights: MIT with a research-use clause), FCPE (MIT) and CREPE (MIT) agree within 50 cents and voicing targets from RMVPE; half is the same speech resynthesised with WORLD, whose f0 is exact (pitch targets only). RMVPE's weights were trained on PTDB-TUG, so its voicing convention may reach Pitchy through those targets; Keele and FDA are free of this. Noise from MUSAN and DEMAND (its Hugging Face mirror, CC BY 4.0), simulated rooms from OpenSLR 28. No speaker of any evaluation set is in the training data. The licence of these weights is not set yet.

The preprint (Pitchy) explains how it was made and measured.

Downloads last month
-
Inference Providers NEW
This model isn't deployed by any Inference Provider. 🙋 Ask for provider support

Collection including Scicom-intl/pitchy