Accura
Public facade for intonation analysis and scoring.
What is Intonation Analysis?
Intonation measures how accurately a singer hits target pitches against a chosen tuning system. Accura builds a pitch histogram from a performance, detects peaks corresponding to sung notes, maps them to target intervals in the tuning system (Equal Temperament or Just Intonation), and reports per-note deviations in cents. An overall 0–100 score can be derived from the analysis via a perceptually-weighted grading scale.
When to Use
| Scenario | Use This? | Why |
|---|---|---|
| Grade pitch accuracy of a sung melody against EQ/JI | Yes | Core use case |
| Per-note deviation breakdown (cents off target) | Yes | analyzePitching returns note-level detail |
| Compare a student against a reference melody | No | Use CalibraMelodyEval |
| Raw pitch histogram without a tuning system | No | Use PitchAnalysis.computeHistogram |
| Voice qualities (breath, agility, range) | No | Use Tessera* facades |
Quick Start
Kotlin
// 1. Extract a pitch contour from audio
val extractor = PitchDetection.createContourExtractor(ContourExtractorConfig.SCORING)
val contour = extractor.extract(audioSamples, sampleRate = 16000)
extractor.release()
// 2. Analyze against a tuning system
val result = Accura.analyzePitching(
contour = contour,
tonicHz = 196.0f, // G3
intonationSystem = IntonationSystem.EQ,
scaleIntervals = listOf( // optional: explicit raaga targets
TargetInterval(0f, "Sa"),
TargetInterval(200f, "Re"),
TargetInterval(400f, "Ga"),
TargetInterval(700f, "Pa"),
TargetInterval(900f, "Da"),
),
)
// 3. Inspect notes and compute an overall score
if (result.error == null) {
val score = Accura.calculateScore(result, weightingMethod = WeightingMethod.EQUAL)
println("Score: ${score.score}/100 over ${score.noteCount} notes")
for (note in result.notes) {
println("${note.label}: ${note.deviationCents} cents (${note.tier})")
}
} else {
println("Analysis inconclusive: ${result.error}")
}Swift
let extractor = PitchDetection.createContourExtractor(config: .scoring)
let contour = extractor.extract(audio: audioSamples, sampleRate: 16000)
extractor.release()
let result = Accura.analyzePitching(
contour: contour,
tonicHz: 196.0, // G3
intonationSystem: .eq,
scaleIntervals: [
TargetInterval(cents: 0, label: "Sa"),
TargetInterval(cents: 200, label: "Re"),
TargetInterval(cents: 400, label: "Ga"),
TargetInterval(cents: 700, label: "Pa"),
TargetInterval(cents: 900, label: "Da"),
]
)
if result.error == nil {
let score = Accura.calculateScore(result: result, weightingMethod: .equal)
print("Score: \(score.score)/100 over \(score.noteCount) notes")
} else {
print("Analysis inconclusive: \(result.error ?? "")")
}Failure Semantics (ADR-022)
Accura follows the SDK rule for public APIs:
| Kind of failure | How it surfaces | Caller action |
|---|---|---|
| Invalid input (caller bug) | IllegalArgumentException | Fix the call site |
| Domain-level inconclusive (valid input, but analysis couldn't produce a score — e.g. < 3 histogram peaks, no target intervals match the scale) | Non-null result with IntonationAnalysisResult.error set | Check result.error and fall back gracefully |
Preconditions enforced by analyzePitching: non-empty contour, tonicHz > 0, non-empty scaleIntervals if provided. See ADR-022 for the rationale.
Common Pitfalls
Unvoiced-heavy or too-short audio: Analysis needs at least three histogram peaks. Short samples return a result with
errorset rather than throwing — this is a domain outcome, not a caller bug.scaleIntervalsare exact (cents, label) targets: Pass the precise intervals of the scale you're grading against and the labels you want in the per-note result. Multi-octave is supported and recommended for sustained-note tools where the user sings across octaves.nullfalls back to the full 12-TET / JI multi-octave grid with chromatic labels derived fromnoteLabelTraditionandtonicHz. Passing an empty list is rejected as a precondition violation.alignTuningshifts the histogram, nottonicHz: Whentrue, a global tuning offset (≈ drift from concert pitch) is estimated and bin centers are shifted. The tonic frequency itself is unchanged and is reported back in the result unmodified.intonationSystemselects target intervals, not an input-tuning assumption. UseEQto grade against 12-TET; useJIto grade against just-intonation ratios. Both are evaluated relative totonicHz.
See also
The per-note result structure returned by analyzePitching
The 0–100 score returned by calculateScore
EQ (12-TET) vs JI (Just Intonation)
Carnatic / Hindustani / Western label conventions
For producing the input contour
Hz / cents / note-label conversions
Functions
Analyze intonation of a vocal performance.
Calculate an overall intonation score (0–100) from analysis results.