Pitch Analysis
Public facade for pitch analysis — histograms, tuning estimation, and melodic transcription (ADR-001).
What is Pitch Analysis?
Once a pitch contour is extracted (PitchDetection) and optionally cleaned (PitchProcessing), analysis turns it into musical information:
Histogram — distribution of pitch values in cents relative to tonic. Basis for peak detection, intonation scoring (Accura), and shruti alignment.
Tuning estimation — how far the performance is from 12-TET grid.
Quantization — snap pitch values to the nearest target intervals (useful for displaying a "piano roll" view of sung notes).
Labelling — segment a contour into tonal regions and label each by its closest target interval (e.g., "Sa", "Ri", "Ga").
Linear fit — fit piecewise linear segments for gamaka / ornament analysis.
When to Use
| Scenario | Method | Why |
|---|---|---|
| Build a pitch histogram for visualization | computeHistogram | Core building block |
| Estimate global tuning offset (concert pitch drift) | estimateTuningOffset | Aligns histogram to 12-TET grid |
| Snap pitches to discrete notes (piano roll) | quantize | Stability-filtered nearest-interval mapping |
| Segment into labelled tonal regions | labelByMeanPitch | Sliding window with majority-vote labelling |
| Analyze gamakas / ornaments with slope | fitLinearSegments | Piecewise linear regression |
| Full intonation analysis + scoring | Use Accura instead | Accura wraps computeHistogram + its own scoring |
Quick Start
Kotlin
val extractor = PitchDetection.createContourExtractor(ContourExtractorConfig.SCORING)
val contour = extractor.extract(audioSamples, 16000)
extractor.release()
val histogram = PitchAnalysis.computeHistogram(contour, tonicHz = 196f)
val tuningOffset = PitchAnalysis.estimateTuningOffset(contour, refFreqHz = 196f)
val targetIntervals = MusicTheory.EQ_TEMPERED_INTERVALS_CENTS_BASE.map { it.toFloat() }.toFloatArray()
val segments = PitchAnalysis.labelByMeanPitch(contour, tonicHz = 196f, targetIntervals)
for (seg in segments) {
println("${seg.label}: ${seg.startSeconds}s – ${seg.endSeconds}s")
}Swift
let histogram = PitchAnalysis.computeHistogram(contour: contour, tonicHz: 196)
let offset = PitchAnalysis.estimateTuningOffset(contour: contour, refFreqHz: 196)
let targets = MusicTheory.eqTemperedIntervalsCentsBase.map { Float($0) }
let segments = PitchAnalysis.labelByMeanPitch(
contour: contour, tonicHz: 196, targetIntervalsCents: targets
)
for seg in segments {
print("\(seg.label ?? "?"): \(seg.startSeconds)s – \(seg.endSeconds)s")
}Common Pitfalls
tonicHzmust be > 0: All methods convert Hz → cents relative to tonic. A zero or negative tonic producesNaNcents.Contour should be processed first: Raw contours with octave errors produce misleading histograms. Run through
PitchProcessing.processwith at leastPitchProcessingConfig.SCORINGbefore analyzing.targetIntervalsCentsis in cents, not Hz: Pass 12-TET or JI interval values (e.g.,[0, 100, 200, ..., 1100]for 12-TET). UseMusicTheory.EQ_TEMPERED_INTERVALS_CENTS_BASEfor convenience.Histogram smoothing is off by default in HistogramConfig.RAW: If peaks look noisy, use HistogramConfig.DEFAULT (sigma = 5).
See also
For creating the contour that feeds into analysis
For cleaning the contour before analysis
For full intonation scoring (wraps this facade)
The histogram type returned by computeHistogram
The segment type returned by labelByMeanPitch and fitLinearSegments
For interval/note-label conversions
Functions
Compute a pitch histogram from a PitchContour.
Estimate global tuning offset by aligning a pitch histogram with the 12-TET grid.
Fit linear segments to a pitch contour using a sliding window.
Label segments of a pitch contour by mean pitch within a sliding window.
Quantize a pitch contour to the nearest target intervals where the pitch is stable.