CalibraVocalRange

Streaming vocal range analyzer for determining a singer's pitch range.

What is Vocal Range?

Vocal range is the span between a singer's lowest and highest notes. This analyzer detects the range using pedagogically-valid criteria based on ASHA 2018 guidelines and voice science research.

Use it for:

  • Onboarding: Determine user's comfortable singing range

  • Song selection: Match songs to user's range

  • Progress tracking: See if range expands over time

  • Key transposition: Calculate optimal key for user

When to Use

ScenarioUse This?Why
Find singer's rangeYesCore use case
Guided range findingYesgetStableNote() for step-by-step
Real-time pitch displayNoUse CalibraPitch
Score singingNoUse CalibraLiveEval

Quick Start

Kotlin

val analyzer = CalibraVocalRange.create()

// Feed audio from recorder
recorder.audioBuffers.collect { buffer ->
analyzer.addAudio(buffer.toFloatArray())
}

// Get detected range
val range = analyzer.getRange()
if (range != null) {
println("Range: ${range.lower.noteLabel} to ${range.upper.noteLabel}")
println("Octaves: ${range.octaves}")
}

analyzer.release()

Swift

let analyzer = CalibraVocalRange.create()

// Feed audio from recorder
for await buffer in recorder.audioBuffersStream() {
analyzer.addAudio(samples: buffer.toFloatArray())
}

// Get detected range
if let range = analyzer.getRange() {
print("Range: \(range.lower.noteLabel) to \(range.upper.noteLabel)")
print("Octaves: \(range.octaves)")
}

analyzer.release()

Guided Range Detection

For step-by-step range finding (e.g., "sing your lowest note"):

Kotlin

val analyzer = CalibraVocalRange.create()

// Phase 1: Detect lowest note
showPrompt("Sing your lowest comfortable note")
// ... user sings, feed audio via addAudio() ...
val lowestNote = analyzer.getStableNote()
analyzer.reset() // Reset for next phase

// Phase 2: Detect highest note
showPrompt("Sing your highest comfortable note")
// ... user sings, feed audio via addAudio() ...
val highestNote = analyzer.getStableNote()

// Calculate range
val rangeOctaves = (highestNote.pitch.midiNote - lowestNote.pitch.midiNote) / 12f

analyzer.release()

Detection Criteria (ASHA 2018)

The analyzer uses these criteria for pedagogical validity:

  • Minimum 1 second sustained pitch for note inclusion

  • Confidence threshold of 0.5 minimum

  • Stability: pitch deviation < 1 semitone within 50ms

  • Percentile-based range extraction (5th-95th percentile)

Platform Notes

iOS

  • Audio is typically 48kHz from microphone; internal resampling handles this

  • Uses YIN algorithm for pitch detection (no external dependencies)

Android

  • Audio varies by device (44.1kHz, 48kHz, 16kHz)

  • Uses YIN algorithm for pitch detection (pure Kotlin)

Common Pitfalls

  1. Forgetting to release: Call analyzer.release() to free resources

  2. Not resetting between phases: Call reset() when starting new detection

  3. Not enough sustained notes: User must hold notes for 1+ second

  4. Background noise: High noise can trigger false detections

See also

For the result structure

For customizing detection parameters

For general pitch detection

Types

Link copied to clipboard
object Companion

Functions

Link copied to clipboard

Add audio samples for analysis.

Link copied to clipboard

Get lower limit from accumulated data (5th percentile of stable pitches).

Link copied to clipboard

Get complete range analysis.

Link copied to clipboard

Get the stable note detected so far.

Link copied to clipboard

Get statistics about accumulated data.

Link copied to clipboard

Get upper limit from accumulated data (95th percentile of stable pitches).

Link copied to clipboard
fun release()

Release resources.

Link copied to clipboard
fun reset()

Reset for new detection session.