{"id":50286571,"url":"https://github.com/denizyesilirmak/react-native-tuner-engine","last_synced_at":"2026-05-28T03:03:59.328Z","repository":{"id":358421220,"uuid":"1240591219","full_name":"denizyesilirmak/react-native-tuner-engine","owner":"denizyesilirmak","description":"a musician-grade react native tuning engine powered by native audio processing and real-time pitch detection.","archived":false,"fork":false,"pushed_at":"2026-05-24T18:58:05.000Z","size":2809,"stargazers_count":1,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-05-24T19:16:39.422Z","etag":null,"topics":["cpp","dsp","fft","jsi","oboe","pitch-detection","turbo-module","yin"],"latest_commit_sha":null,"homepage":"","language":"C++","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/denizyesilirmak.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":"CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-05-16T10:17:59.000Z","updated_at":"2026-05-24T19:07:17.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/denizyesilirmak/react-native-tuner-engine","commit_stats":null,"previous_names":["denizyesilirmak/react-native-tuner-engine"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/denizyesilirmak/react-native-tuner-engine","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/denizyesilirmak%2Freact-native-tuner-engine","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/denizyesilirmak%2Freact-native-tuner-engine/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/denizyesilirmak%2Freact-native-tuner-engine/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/denizyesilirmak%2Freact-native-tuner-engine/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/denizyesilirmak","download_url":"https://codeload.github.com/denizyesilirmak/react-native-tuner-engine/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/denizyesilirmak%2Freact-native-tuner-engine/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":33592074,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-05-28T02:00:06.440Z","response_time":99,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["cpp","dsp","fft","jsi","oboe","pitch-detection","turbo-module","yin"],"created_at":"2026-05-28T03:03:59.216Z","updated_at":"2026-05-28T03:03:59.323Z","avatar_url":"https://github.com/denizyesilirmak.png","language":"C++","funding_links":[],"categories":[],"sub_categories":[],"readme":"# react-native-tuner-engine\n\n[![npm version](https://img.shields.io/npm/v/react-native-tuner-engine)](https://www.npmjs.com/package/react-native-tuner-engine)\n[![npm downloads](https://img.shields.io/npm/dm/react-native-tuner-engine)](https://www.npmjs.com/package/react-native-tuner-engine)\n[![license](https://img.shields.io/npm/l/react-native-tuner-engine)](LICENSE)\n[![platform](https://img.shields.io/badge/platform-iOS%20%7C%20Android-lightgrey)]()\n\nA React Native Turbo Module for real-time instrument pitch detection. The detection pipeline runs entirely in C++ on a dedicated audio thread and delivers per-frame results to JavaScript via the New Architecture event system.\n\nRequires React Native **0.75 or later** with the New Architecture enabled.\n\n## How it works\n\nAudio is captured through platform-native APIs (AVAudioEngine on iOS, Oboe on Android) and fed into a lock-free SPSC ring buffer. A C++ worker thread drains the buffer in fixed-size frames, runs the signal through a preprocessing pipeline, then through an ensemble of three pitch detectors (YIN, PYIN, cepstrum). The ensemble votes for the best estimate and fires an `onPitch` event on the JS thread.\n\nThe audio callback allocates nothing at runtime — all working buffers are pre-allocated during initialization.\n\n## Installation\n\n```sh\nnpm install react-native-tuner-engine\n# or\nyarn add react-native-tuner-engine\n```\n\n### Expo\n\nWorks with Expo **development builds** (not Expo Go). Add the plugin to your `app.json`:\n\n```json\n{\n  \"expo\": {\n    \"plugins\": [\"react-native-tuner-engine\"]\n  }\n}\n```\n\nTo customize the iOS microphone permission message:\n\n```json\n{\n  \"expo\": {\n    \"plugins\": [\n      [\"react-native-tuner-engine\", {\n        \"microphonePermission\": \"Microphone access is required for pitch detection.\"\n      }]\n    ]\n  }\n}\n```\n\nThen rebuild with `npx expo prebuild` or EAS Build.\n\n### Bare React Native — iOS\n\nAdd a microphone usage description to your app's `Info.plist`:\n\n```xml\n\u003ckey\u003eNSMicrophoneUsageDescription\u003c/key\u003e\n\u003cstring\u003eMicrophone access is required for pitch detection.\u003c/string\u003e\n```\n\nThen run `pod install`.\n\n### Bare React Native — Android\n\nThe library's `AndroidManifest.xml` already declares `RECORD_AUDIO`. You still need to request the permission at runtime — use `requestPermission()` described below, or handle it yourself before calling `start()`.\n\n## Quick start\n\n```tsx\nimport { useTuner } from 'react-native-tuner-engine';\n\nexport function TunerScreen() {\n  const { start, stop, latest, isRunning, error } = useTuner({\n    noiseGateDb: -50,\n    confidenceThreshold: 0.75,\n  });\n\n  return (\n    \u003c\u003e\n      \u003cText\u003e{latest?.noteName}{latest?.octave} {latest?.cents.toFixed(1)} ¢\u003c/Text\u003e\n      \u003cButton title={isRunning ? 'Stop' : 'Start'} onPress={isRunning ? stop : start} /\u003e\n    \u003c/\u003e\n  );\n}\n```\n\n`useTuner` requests the microphone permission, configures the engine, and subscribes to events — all in one call.\n\n## API\n\n### `useTuner(options?)`\n\nReact hook. Returns `{ start, stop, latest, isRunning, error }`.\n\n```typescript\nimport { useTuner } from 'react-native-tuner-engine';\n\nconst { start, stop, latest, isRunning, error } = useTuner({\n  // All fields optional — defaults shown\n  sampleRate?: number;          // 48000\n  frameSize?: number;           // 2048\n  noiseGateDb?: number;         // -55 dBFS\n  confidenceThreshold?: number; // 0.75  (0–1)\n  minFrequency?: number;        // 60 Hz\n  maxFrequency?: number;        // 1200 Hz\n  instrument?: Instrument;      // 'chromatic'\n  a4?: number;                  // 440 Hz\n\n  // DSP tuning\n  emaAlpha?: number;            // 0.35  — smoothing (0.05 = slow/stable, 1.0 = instant)\n  hysteresisFrames?: number;    // 3     — frames before a note change is confirmed (1–10)\n  hpfCutoffHz?: number;         // 70 Hz — high-pass filter cutoff (30 for bass, 100 for violin)\n});\n```\n\n`start()` — requests mic permission, configures the engine, then begins capture. Throws if permission is denied.\n\n`stop()` — stops capture. Safe to call when already stopped.\n\n`latest` — the most recent `PitchEvent`, or `null` before the first frame.\n\n`error` — set if `start()` throws; `null` otherwise.\n\n### `TunerEngine`\n\nLow-level imperative API, useful outside of React components.\n\n```typescript\nimport { TunerEngine } from 'react-native-tuner-engine';\n\nawait TunerEngine.requestPermission(); // → boolean\nawait TunerEngine.configure({ noiseGateDb: -50 });\nawait TunerEngine.start();\n\nconst unsub = TunerEngine.onPitch((event) =\u003e {\n  if (event.hasPitch) console.log(event.noteName, event.cents);\n});\n\n// later:\nunsub();\nawait TunerEngine.stop();\n```\n\n### Types\n\n```typescript\ntype PitchEvent = {\n  hasPitch: boolean;\n  frequency: number;  // Hz\n  confidence: number; // 0–1\n  rmsDb: number;      // dBFS\n  noteName: string;   // e.g. \"A\"\n  octave: number;\n  cents: number;      // −50 to +50 relative to equal temperament\n};\n\ntype TunerConfig = {\n  sampleRate?: number;          // default 48000\n  frameSize?: number;           // default 2048\n  noiseGateDb?: number;         // default -55 dBFS\n  confidenceThreshold?: number; // default 0.75\n  minFrequency?: number;        // default 60 Hz\n  maxFrequency?: number;        // default 1200 Hz\n  a4?: number;                  // default 440 Hz\n  emaAlpha?: number;            // default 0.35 — PostProcessor smoothing (0.05–1.0)\n  hysteresisFrames?: number;    // default 3    — frames to confirm a note change (1–10)\n  hpfCutoffHz?: number;         // default 70   — high-pass filter cutoff in Hz (20–300)\n  onsetDetection?: boolean;     // default false — resets smoothing on note attacks\n};\n\ntype Instrument =\n  | 'guitar' | 'bass' | 'violin' | 'viola' | 'cello'\n  | 'ukulele' | 'mandolin' | 'banjo' | 'chromatic';\n\ntype Temperament = 'equal' | 'just';\n```\n\n### Additional methods on `TunerEngine`\n\n```typescript\nTunerEngine.setA4(hz: number): void          // default 440\nTunerEngine.setInstrument(name: Instrument): void\nTunerEngine.setTemperament(name: Temperament): void\nTunerEngine.getStatus(): { isRunning: boolean; engineReady: boolean }\n```\n\nFor the full API reference (all config options, quality presets, adaptive frame size, overlap ratio), see **[documents/API.md](documents/API.md)**.\n\n## C++ pipeline\n\nThe shared C++ core (`cpp/`) compiles as a static library on both platforms.\n\n| Stage | Class | Notes |\n|---|---|---|\n| High-pass filter | `BiquadHpf` | Direct-Form II Transposed, 70 Hz cutoff (configurable), Q 0.707 |\n| Windowing | `Window` | Hann window, precomputed coefficients |\n| Pitch detection | `EnsembleSelector` | Runs YIN, PYIN, and cepstrum; votes by agreement within 1 semitone |\n| — detector 1 | `YinPitchDetector` | YIN with parabolic interpolation |\n| — detector 2 | `PyinPitchDetector` | Probabilistic YIN; prunes harmonic aliases |\n| — detector 3 | `CepstrumPitchDetector` | Real cepstrum via radix-2 FFT; SNR-based confidence |\n| Note mapping | `NoteMapper` | Hz → MIDI, note name, octave, cents deviation |\n| SNR estimation | `SnrEstimator` | Signal RMS vs. noise-floor EMA |\n| Post-processing | `PostProcessor` | Median-5 filter, EMA smoothing (configurable), note-transition hysteresis (configurable) |\n| Dispatch | `AudioFrameDispatcher` | SPSC lock-free queue, dedicated worker thread |\n\n## Performance targets\n\n| Metric | Target |\n|---|---|\n| Time to first pitch | \u003c 300 ms |\n| Per-frame CPU (midrange Android) | \u003c 5 ms |\n| Per-frame CPU (iPhone 12+) | \u003c 3 ms |\n| Audio-thread allocations | 0 |\n\n## Requirements\n\n- React Native 0.75+ (New Architecture / Bridgeless)\n- iOS 13+\n- Android API 24+, NDK r26+\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdenizyesilirmak%2Freact-native-tuner-engine","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fdenizyesilirmak%2Freact-native-tuner-engine","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdenizyesilirmak%2Freact-native-tuner-engine/lists"}