# pitch-detector ## Purpose `pitch-detector.ts` exposes the `PitchDetector` class — the Analysis Op plugin (plugin slot 3 in the `@suaveplan/audio-*` family) that estimates the fundamental frequency of an `AudioDocument`. It supports whole-document analysis (`analyze`), streaming refinement (`analyzeStream`), and synchronous single-block analysis (`analyzeBlock`) for real-time visualisation. Three estimators are selectable per call — YIN (default), McLeod, and cepstrum — letting callers pick the right trade-off for the material at hand without coupling to a different package. ## Features - Three pitch algorithms behind the `algorithm` option. - Configurable frequency range via `minFreq` / `maxFreq` and frame size. - `AbortSignal` support across all three entry points. - Zod boundary validation on every options object. - OpenTelemetry spans per detector run, counter for total runs, histograms for run and block duration. - Stable error codes (`INVALID_OPTIONS`, `ABORTED`, `UNSUPPORTED_FORMAT`, `INSUFFICIENT_DATA`) all routable through `AudioAnalysisError`. ## Usage: Basic ```ts import { PitchDetector } from "@suaveplan/audio-analysis-pitch"; const detector = new PitchDetector(); const result = await detector.analyze(doc); console.log(result.pitch, "Hz", "midi:", result.midiNote); ``` ## Usage: Advanced ```ts import { PitchDetector } from "@suaveplan/audio-analysis-pitch"; const detector = new PitchDetector(); const controller = new AbortController(); const opts = { algorithm: "mcleod" as const, minFreq: 50, maxFreq: 1_000, signal: controller.signal }; const out: number[] = []; for await (const r of detector.analyzeStream(doc, opts)) { out.push(r.pitch); if (out.length >= 100) controller.abort(); } ``` ## API Reference - `class PitchDetector { name: "pitch-detector"; version: string; description: string; analyze(doc, opts?); analyzeStream(doc, opts?); analyzeBlock(block, ctx) }`. - `interface PitchDetectorOptions { algorithm?, minFreq?, maxFreq?, frameSize?, hopSize?, signal? }`. - `interface PitchResult extends AnalysisResult { op: "pitch-detector"; pitch; midiNote; confidence; timestamp; duration }`. ## Implementation Notes `analyze` aggregates results by keeping the highest-confidence pitch across all frames. `analyzeStream` yields per-block estimates with a running max confidence so the stream is non-decreasing in expectation. Both honour an `AbortSignal` checked at every block boundary. `analyzeBlock` is allocation-light: it skips the Zod schema (the block path is hot) and uses the package defaults.