Pitch Processing
Public facade for batch pitch post-processing — cleanup, correction, smoothing, and segmentation (ADR-001).
What is Pitch Processing?
Raw pitch contours from PitchDetection contain octave errors, short noise bursts (blips), and jitter. This facade provides a config-driven pipeline that cleans them up, plus individual operations for building custom pipelines.
Pipeline order (each stage skipped if disabled in config): octave correction → blip removal → smoothing.
When to Use
| Scenario | Use | Why |
|---|---|---|
| Clean a contour for scoring / melody eval | process(contour, .SCORING) | Octave fix + blip removal, no smoothing |
| Clean a contour for visualization | process(contour, .DISPLAY) | All stages, shorter min-duration |
| Get raw pitch unchanged | process(contour, .RAW) | No-op passthrough |
| Build a custom cleanup chain | Individual ops (correctOctaveErrors, smooth, medianFilter, ...) | Mix and match |
| Realtime per-frame processing | Not this facade — realtime smoothing/correction is handled inside PitchDetector when enabled via PitchDetectorConfig.Builder.enableProcessing() | Different budget (ADR-020) |
Usage Tiers
// Tier 1: Preset (80% of users)
val cleaned = PitchProcessing.process(contour, PitchProcessingConfig.SCORING)
// Tier 2: Builder (15%)
val config = PitchProcessingConfig.Builder()
.fixOctaveErrors(true)
.removeSpuriousJumps(false)
.smoothingWindowSize(9)
.build()
val cleaned = PitchProcessing.process(contour, config)
// Tier 3: .copy() (5%)
val cleaned = PitchProcessing.process(contour, PitchProcessingConfig.DISPLAY.copy(hopMs = 20))
// Custom pipeline (individual operations)
var pitches = contour.toPitchesArray()
pitches = PitchProcessing.correctOctaveErrors(pitches, OctaveCorrectionConfig.BASIC)
pitches = PitchProcessing.iqrFilter(pitches)
pitches = PitchProcessing.smooth(pitches)iOS (Swift)
// Tier 1: Preset (native [Float] arrays, no KotlinFloatArray needed)
let cleaned = PitchProcessing.process(contour: contour, config: .scoring)
// Tier 2: Builder
let config = PitchProcessingConfig.Builder()
.fixOctaveErrors(true)
.removeSpuriousJumps(false)
.smoothingWindowSize(9)
.build()
let cleaned = PitchProcessing.process(contour: contour, config: config)
// Custom pipeline (individual operations with native [Float])
var pitches = contour.toPitchesArray().toSwiftArray()
pitches = PitchProcessing.correctOctaveErrors(pitchesHz: pitches, config: .basic)
pitches = PitchProcessing.iqrFilter(pitchesHz: pitches)
pitches = PitchProcessing.smooth(pitchesHz: pitches)Common Pitfalls
smoothandmedianFilterrequire odd window/kernel size: Even values throwIllegalArgumentException(ADR-022). Defaults are 7 and 5 respectively.Unvoiced frames use -1, not 0: All operations preserve the
-1sentinel for unvoiced frames. Don't zero-fill before processing.hopMsmatters for duration-based ops:removeBlipsandinterpolateSilenceconvert millisecond thresholds to frame counts viahopMs. Wrong hop → wrong durations.Batch processing, not realtime: This facade processes entire arrays. For per-frame realtime processing, use PitchDetector with
enableProcessing()in PitchDetectorConfig.Builder.
See also
For creating detectors and extractors that produce contours
For histogram, quantization, and transcription on processed contours
The config class with presets (RAW, SCORING, DISPLAY)
Standalone config for the octave correction stage
Functions
Apply config-driven octave correction.
Apply DBSCAN-based outlier filter.
Find contiguous segments where a mask is true.
Get mask filtering short voiced segments below minimum duration.
Get mask of frames with stable pitch slope.
Get mask of valid (voiced) pitch values.
Interpolate over silence gaps in a pitch contour.
Apply IQR-based outlier filter.
Apply median filter for spike removal.
Process a pitch contour with preset or custom config.
Process a pitch array with preset or custom config.
Apply frequency range filter.
Remove short voiced runs (blips) below minimum duration.
Resample a pitch sequence to a different hop rate.
Apply weighted average smoothing in cents space.
Apply Gaussian smoothing with NaN-aware gap interpolation.