CalibraLiveEval

Real-time singing evaluation session with segment support.

What is Live Evaluation?

Live evaluation scores a singer's performance in real-time by comparing their pitch to a reference melody. Use it for:

  • Karaoke apps: Score singing as users perform

  • Music education: Provide instant feedback on pitch accuracy

  • Practice apps: Track improvement across attempts

The session breaks a song into segments (phrases or sections), evaluates each one, and tracks the user's progress.

When to Use

ScenarioUse This?Why
Score singing against referenceYesCore use case
Just detect pitch (no scoring)NoUse CalibraPitch.createDetector()
Analyze recorded audio (not live)NoUse CalibraMelodyEval
Voice activity detection onlyNoUse CalibraVAD

Quick Start

Kotlin

// 1. Create detector and session
val detector = CalibraPitch.createDetector()
val session = CalibraLiveEval.create(lessonMaterial, detector = detector)

// 2. Prepare (loads reference, extracts features)
session.prepareSession()

// 3. Start segment and feed audio
session.startPracticingSegment(0)
recorder.audioBuffers.collect { buffer ->
session.feedAudioSamples(buffer.toFloatArray(), sampleRate = 48000)
}

// 4. Get result
val result = session.finishPracticingSegment()
println("Score: ${result?.score}")

// 5. Cleanup
session.closeSession()

Swift

// 1. Create detector and session
let detector = CalibraPitch.createDetector()
let session = CalibraLiveEval.create(
reference: lessonMaterial,
detector: detector
)

// 2. Prepare (loads reference, extracts features)
try await session.prepareSession()

// 3. Start segment and feed audio
session.startPracticingSegment(index: 0)
for await buffer in recorder.audioBuffers {
session.feedAudioSamples(samples: buffer.toFloatArray(), sampleRate: 48000)
}

// 4. Get result
if let result = session.finishPracticingSegment() {
print("Score: \(result.score)")
}

// 5. Cleanup
session.closeSession()

Usage Tiers

Tier 1: Convenience API (80% of users)

Pass player and recorder handles; the session coordinates everything.

val session = CalibraLiveEval.create(
reference = lessonMaterial,
detector = CalibraPitch.createDetector(),
player = player,
recorder = recorder
)
session.prepareSession()
session.onSegmentComplete { result -> showScore(result) }
session.startPracticingSegment(0) // Seeks, plays, records, scores automatically

Tier 2: Low-Level API (15% of users)

Manually manage audio; full control over timing.

val session = CalibraLiveEval.create(reference, detector = detector)
session.prepareSession()
session.startPracticingSegment(0)
recorder.audioBuffers.collect { buffer ->
session.feedAudioSamples(buffer.toFloatArray(), buffer.sampleRate)
}
val result = session.finishPracticingSegment()

Phase Progressions

  • Singalong: IDLE → SINGING → EVALUATED

  • Singafter: IDLE → LISTENING → SINGING → EVALUATED

Observe phase StateFlow for UI updates.

State Machine

IDLE ──prepareSession()──► READY
READY ──startPracticingSegment()──► PRACTICING
PRACTICING ──finishPracticingSegment()──► BETWEEN (or COMPLETED if last)
PRACTICING ──discardCurrentSegment()──► BETWEEN
PRACTICING ──seekToSegment()──► PRACTICING (new segment)
BETWEEN ──startPracticingSegment()──► PRACTICING
BETWEEN ──advanceToNextSegment()──► PRACTICING (or COMPLETED if last)
BETWEEN ──finishSession()──► COMPLETED
* ──closeSession()──► (released)

Ownership Model

DependencyOwnershipRationale
detectorOwned - session closes itCreated specifically for this session
playerBorrowed - caller managesShared resource, UI may need direct access
recorderBorrowed - caller managesShared resource, may be reused

See also

For pitch detection without scoring

Input format with reference audio and segments

Configuration for auto-advance, thresholds, etc.

Per-segment scoring results

Types

Link copied to clipboard
object Companion

Properties

Link copied to clipboard

Current active segment state, or null if not practicing.

Link copied to clipboard

Map of segment index to list of attempts (from SegmentResultStore).

Link copied to clipboard
val currentTime: StateFlow<Float>

Current playback position in seconds (from player or clock in manual mode).

Link copied to clipboard
val isPlaying: StateFlow<Boolean>

Whether the player is currently playing.

Link copied to clipboard
val isRecording: StateFlow<Boolean>

Whether recording is active.

Link copied to clipboard
val livePitch: StateFlow<PitchPoint>

Real-time pitch point for visualization (includes time and confidence).

Link copied to clipboard

Live pitch contour accumulated during the current segment.

Link copied to clipboard
val phase: StateFlow<PracticePhase>

Current practice phase. Observe this for unified singalong/singafter UI.

Link copied to clipboard

Whether pitch processing is currently enabled

Link copied to clipboard

Reference key in Hz from LessonMaterial.

Link copied to clipboard

All segments from the reference.

Link copied to clipboard
val state: StateFlow<SessionState>

Current session state. Observe this in your UI.

Link copied to clipboard

Current student key in Hz. 0 = same as reference.

Functions

Link copied to clipboard

Advance to the next segment.

Link copied to clipboard

Manually trigger LISTENING → SINGING transition.

Link copied to clipboard
open override fun close()

Release all resources.

Link copied to clipboard

Close the session and release all resources.

Link copied to clipboard

Discard the current segment without scoring.

Link copied to clipboard
fun feedAudioSamples(samples: FloatArray, sampleRate: Int = 16000)

Feed audio samples to the session.

Link copied to clipboard

Finish the current segment and get its result.

Link copied to clipboard

Finish the session and get aggregated results.

Link copied to clipboard

Get all results for a specific segment.

Link copied to clipboard

Check if a segment has been completed at least once.

Link copied to clipboard

Register callback for phase changes. Called when practice phase transitions (e.g., LISTENING → SINGING).

Link copied to clipboard

Register callback for reference end (singafter mode). Called when reference audio finishes playing, before student starts singing.

Link copied to clipboard

Register callback for segment completion. Called when a segment finishes with its result.

Link copied to clipboard

Register callback for session completion. Called when all segments are finished.

Link copied to clipboard

Pause playback and recording.

Link copied to clipboard
suspend fun prepareSession()

Prepare the session for practice.

Link copied to clipboard
fun restartSession(fromSegment: Int = 0)

Restart the session from a clean state.

Link copied to clipboard

Resume from paused state.

Link copied to clipboard

Retry the current segment.

Link copied to clipboard
fun seekToSegment(index: Int)

Seek to a specific segment (discards current attempt if practicing).

Link copied to clipboard
fun seekToTime(seconds: Float)

Seek to a specific time position.

Link copied to clipboard

Enable or disable pitch processing (smoothing + octave correction) at runtime.

Link copied to clipboard

Set student key for transposition.

Link copied to clipboard

Start practicing a specific segment.