A real-time pitch detection library for React Native with 6 classical algorithms. Cross-platform shared C++ core for iOS and Android.
- 6 algorithms: ACF2+, YIN, MPM, HPS, AMDF, RAPT
- New Architecture (TurboModules) and Old Architecture support
- Zero dependencies: No ML frameworks, no ONNX — pure C++ signal processing
- YIN-FFT: O(N log N) classical pitch detection — low latency, sub-cent precision
- MPM: McLeod Pitch Method — bounded NSDF with reliable threshold selection
- RAPT: Two-stage pitch tracking — gold standard for speech processing
- Cross-platform: Shared C++ core for iOS and Android
npm install react-native-pitchynpx pod-installRebuild your app after installing the package.
Microphone permissions are required. Use react-native-permissions or similar.
- iOS:
Microphone - Android:
RECORD_AUDIO
import Pitchy from 'react-native-pitchy';
// Initialize with algorithm choice
Pitchy.init({
algorithm: 'YIN', // 'ACF2+' | 'YIN' | 'MPM' | 'HPS' | 'AMDF' | 'RAPT'
bufferSize: 4096,
minVolume: -60,
});
// Start detection
await Pitchy.start();
// Listen to pitch events
const subscription = Pitchy.addListener(({ pitch, confidence, volume }) => {
console.log(`Pitch: ${pitch} Hz, Confidence: ${confidence}, Volume: ${volume} dB`);
});
// Stop detection
await Pitchy.stop();
subscription.remove();| Algorithm | Type | Best For | Latency | Accuracy |
|---|---|---|---|---|
YIN |
Classical (FFT) | Guitar tuning | ~1.3ms | 86.0% RPA |
RAPT |
Two-stage NCCF | Speech processing | ~2.5ms | 83.7% RPA |
ACF2+ |
Classical | General purpose | ~4.2ms | 81.4% RPA |
MPM |
Classical (NSDF) | Instrument tuning | ~1.4ms | 79.1% RPA |
AMDF |
Time domain | Low-power devices | ~1.7ms | 74.4% RPA |
HPS |
Frequency domain | Harmonic-rich signals | ~0.3ms | 55.8% RPA |
FFT-accelerated autocorrelation with cumulative mean normalized difference. Best for instrument tuning where low latency and high precision on clean signals matter.
Uses the Normalized Squared Difference Function (NSDF) bounded to [-1, 1], making threshold selection reliable. Key-maxima peak picking with parabolic interpolation. Excellent for musical instruments — used in many professional tuner apps.
Frequency-domain approach that multiplies downsampled magnitude spectra to amplify the fundamental frequency. Fast and effective for harmonic-rich signals (guitar, bass, brass). Uses Hanning windowing and log-domain parabolic interpolation.
No-multiply pitch detection — uses absolute differences instead of multiplications, making it extremely fast on low-power hardware. Parabolic interpolation for sub-bin precision, with confidence derived from the depth of the AMDF minimum. Best for scenarios where CPU budget is tight.
Two-stage pitch detection: first a coarse search on 4x downsampled signal for fast candidate generation, then refined search on the original signal near coarse candidates. Uses Normalized Cross-Correlation Function (NCCF) at both stages. Gold standard for speech processing — robust voiced/unvoiced detection.
Measured on Apple Silicon, 4096 samples @ 44.1kHz, 1000 iterations:
| Algorithm | ms/call | FPS | Clean RPA | Noisy RPA | Notes |
|---|---|---|---|---|---|
| HPS | 0.31 | 3198 | 100%* | 42.1% | Fastest — frequency domain |
| YIN | 1.34 | 744 | 100% | 100% | FFT-accelerated CMND |
| MPM | 1.35 | 743 | 100% | 100% | FFT-based NSDF |
| AMDF | 1.69 | 591 | 100% | 100% | Time-domain, no FFT |
| RAPT | 2.48 | 403 | 100% | 100% | Two-stage NCCF |
| ACF2+ | 4.24 | 236 | 100% | 89.5% | Legacy time-domain |
* HPS requires harmonic-rich signals (100% on instrument-like signals, 0% on pure sine).
All algorithms run well within real-time requirements (a 4096-sample buffer at 44.1kHz represents ~93ms of audio).
Tested on 43 real recordings: 32 orchestral instruments from Philharmonia Orchestra (CC BY-SA 3.0) + 11 male singing voice from Human Voice Dataset (MIT):
| Algorithm | Cello | Guitar | Flute | Clarinet | Fr. Horn | Voice | Overall RPA |
|---|---|---|---|---|---|---|---|
| YIN | 100% | 100% | 100% | 100% | 100% | 73% | 86.0% |
| RAPT | 100% | 100% | 100% | 100% | 100% | 73% | 83.7% |
| ACF2+ | 100% | 83% | 100% | 100% | 100% | 64% | 81.4% |
| MPM | 100% | 83% | 100% | 100% | 100% | 64% | 79.1% |
| AMDF | 100% | 67% | 100% | 100% | 100% | 73% | 74.4% |
| HPS | 40% | 83% | 100% | 50% | 50% | 45% | 55.8% |
RPA = Raw Pitch Accuracy (within 50 cents of ground truth). Voice failures are octave errors on higher male voice notes (A3+) where the second harmonic dominates the fundamental — a known limitation of classical pitch detectors.
react-native-pitchy-pro adds 6 advanced algorithms including ML-powered pitch detection:
| Algorithm | Type | ms/call | Accuracy | Best For |
|---|---|---|---|---|
| CREPE | ML (CNN) | — | ~97.8% RPA* | Highest accuracy, state-of-the-art |
| PESTO | ML (Lightweight) | — | ~96.1% RPA* | Fast ML, only 29K params |
| SwiftF0 | ML (CNN) | — | ~93% RPA* | Noise-robust environments |
| pYIN | HMM + Viterbi | 1.27ms | 100% RPA | Vocal melody extraction |
| Salience | Harmonic Analysis | 0.39ms | 100% RPA | Instrument tuning with tracking |
| SWIPE' | ERB Matching | 0.11ms | 100% RPA | Noise-robust classical |
* ML accuracy from published papers (Kim et al. 2018, Riou et al. 2023). ML latency depends on device and ONNX Runtime.
Pro features:
- 3 ML models via ONNX Runtime (CREPE, PESTO, SwiftF0)
- pYIN with HMM temporal smoothing for stable vocal tracking
- Salience with pitch tracking, octave correction, and ERB weighting
- SWIPE' with sawtooth harmonic template matching
Learn more | Contact for licensing
PitchyAlgorithm:'ACF2+' | 'YIN' | 'MPM' | 'HPS' | 'AMDF' | 'RAPT'PitchyConfig:bufferSize?: Audio buffer size (default: 4096)minVolume?: Minimum volume threshold in dB (default: -60)algorithm?: Detection algorithm (default:'ACF2+')
PitchyEvent:pitch: Detected pitch in Hz (-1 if no pitch)confidence: Detection confidence (0.0 - 1.0)volume: Input volume in dB
PitchyEventCallback:(event: PitchyEvent) => void
Pitchy.init(config?)— Initialize with optional configurationPitchy.start()— Start pitch detection (Promise)Pitchy.stop()— Stop pitch detection (Promise)Pitchy.isRecording()— Check if running (Promise<boolean>)Pitchy.addListener(callback)— Listen toonPitchDetectedevents
See the example app for a working debug implementation with volume metering, raw data display, and algorithm switching.
See the contributing guide.
MIT — Copyright (c) 2024 App Vision