Skip to main content

Utilities & Shared Types

Shared model types, error types, and time utilities used across Calibra APIs.

LessonMaterial

Reference material for singing evaluation. Contains the reference audio, segment boundaries, and musical key.

Creating LessonMaterial

From File

val material = LessonMaterial.fromFile(
audioPath = "/path/to/reference.mp3",
segments = segments,
keyHz = 261.63f // Middle C
)
let material = LessonMaterial.fromFile(
audioPath: "/path/to/reference.mp3",
segments: segments,
keyHz: 261.63
)

From Audio Samples

val material = LessonMaterial.fromAudio(
samples = audioSamples,
sampleRate = 16000,
segments = segments,
keyHz = 196.0f,
pitchContour = precomputedContour, // Optional: enables fast path
hpcpFrames = precomputedHpcp // Optional: for DTW alignment
)
let material = LessonMaterial.fromAudio(
samples: audioSamples,
sampleRate: 16000,
segments: segments,
keyHz: 196.0,
pitchContour: precomputedContour,
hpcpFrames: precomputedHpcp
)

Properties

PropertyTypeDescription
audioSourceAudioSourceSource of the reference audio
segmentsList<Segment>List of segments with timing and lyrics
keyHzFloatMusical key frequency in Hz (e.g., 261.63 for middle C)
pitchContourPitchContour?Pre-computed pitch contour (enables fast path, skipping YIN extraction)
hpcpFramesList<FloatArray>?Pre-computed HPCP frames for DTW alignment (each frame is 12 floats)
durationFloatTotal duration based on the last segment's end time
segmentCountIntNumber of segments

AudioSource

Represents the source of audio data for evaluation. A sealed class with three variants.

VariantPropertiesDescription
AudioSource.Filepath: StringAudio from a local file path
AudioSource.Urlurl: StringAudio from a URL (for future streaming support)
AudioSource.Samplessamples: FloatArray, sampleRate: IntRaw audio samples already in memory (default 16000 Hz)

Segment

A segment of a song or exercise with timing and optional lyrics. Supports both singalong (student sings with reference) and singafter (student sings after reference) modes.

Creating Segments

Kotlin

// Single segment
val segment = Segment(
index = 0,
startSeconds = 0.0f,
endSeconds = 5.0f,
lyrics = "Sa Re Ga Ma"
)

// Singafter segment (student sings after reference)
val segment = Segment(
index = 0,
startSeconds = 0.0f,
endSeconds = 5.0f,
lyrics = "Sa Re Ga Ma",
studentStartSeconds = 2.5f,
studentEndSeconds = 5.0f
)

// From parallel arrays
val segments = Segment.fromArrays(
starts = floatArrayOf(0f, 5f, 10f),
ends = floatArrayOf(5f, 10f, 15f),
lyrics = listOf("Line 1", "Line 2", "Line 3")
)

Swift

// Single segment
let segment = Segment.create(
index: 0,
startSeconds: 0.0,
endSeconds: 5.0,
lyrics: "Sa Re Ga Ma"
)

// Singafter segment
let segment = Segment.create(
index: 0,
startSeconds: 0.0,
endSeconds: 5.0,
lyrics: "Sa Re Ga Ma",
studentStartSeconds: 2.5,
studentEndSeconds: 5.0
)

Properties

PropertyTypeKotlinSwiftDescription
indexIntIntInt (via extension)Zero-based index of the segment
startSecondsFloatFloatDouble (via extension)Reference audio start time in seconds
endSecondsFloatFloatDouble (via extension)Reference audio end time in seconds
lyricsStringStringStringText/lyrics for this segment
studentStartSecondsFloat?Float?Float?When student recording starts (null = same as startSeconds)
studentEndSecondsFloat?Float?Float?When student recording ends (null = same as endSeconds)
durationFloatFloatDouble (via extension)Duration of the segment in seconds
isSingafterBooleanBooleanBoolTrue if student starts after reference
effectiveStudentStartFloatFloatDouble (via extension)Effective student start time (falls back to segment start)
effectiveStudentEndFloatFloatDouble (via extension)Effective student end time (falls back to segment end)
studentDurationFloatFloatDouble (via extension)Duration of the student recording portion

Factory Methods

MethodDescription
Segment.fromArrays(starts, ends, lyrics, studentStarts, studentEnds)Create segments from parallel arrays of start and end times

SegmentResult

Result of evaluating a single segment.

Properties

PropertyTypeDescription
segmentSegmentThe segment that was evaluated
scoreFloatOverall score for this segment (0.0 - 1.0)
pitchAccuracyFloatPitch accuracy component of the score (0.0 - 1.0)
levelPerformanceLevelPerformance level classification
attemptNumberIntWhich attempt this is (1-based, for retry tracking)
referencePitchPitchContourReference pitch contour for visualization
studentPitchPitchContourStudent pitch contour for visualization
isPassingBooleanTrue if score >= 0.5
isGoodBooleanTrue if score >= 0.7
isExcellentBooleanTrue if score >= 0.9
scorePercentIntScore as a percentage (0-100)
feedbackMessageStringHuman-readable feedback based on performance level

Swift Pitch Data Extensions

In Swift, pitch contour data is accessed via tuple extensions:

let result: SegmentResult = ...

// Reference pitch data
let ref = result.referencePitchData
// ref.times: [Float], ref.pitchesHz: [Float], ref.pitchesMidi: [Float]

// Student pitch data
let student = result.studentPitchData
// student.times: [Float], student.pitchesHz: [Float], student.pitchesMidi: [Float]

Factory Method

val result = SegmentResult.create(
segment = segment,
score = 0.85f,
pitchAccuracy = 0.82f,
attemptNumber = 2,
referencePitch = refContour,
studentPitch = studentContour
)

SingingResult

Complete result of a singing evaluation session, aggregating results across all segments.

Properties

PropertyTypeDescription
overallScoreFloatAggregate score across all segments (0.0 - 1.0)
segmentResultsMap<Int, List<SegmentResult>>Map of segment index to list of attempts
aggregationResultAggregationHow the overall score was calculated
overallScorePercentIntOverall score as a percentage (0-100)
segmentCountIntNumber of segments evaluated
totalAttemptsIntNumber of total attempts across all segments
allPassingBooleanTrue if all segments pass (score >= 0.5)

Methods

MethodReturn TypeDescription
latestScorePerSegment()Map<Int, Float>Get the latest score for each segment
bestScorePerSegment()Map<Int, Float>Get the best score for each segment
averageScorePerSegment()Map<Int, Float>Get the average score for each segment
latestResultPerSegment()Map<Int, SegmentResult>Get the latest result for each segment
getAllFeedback()List<String>Get feedback messages for all segments

Swift Convenience Methods

let result: SingingResult = ...

// Latest score for a specific segment
if let score = result.latestScore(forSegment: 0) {
print("Score: \(Int(score * 100))%")
}

// Best score for a specific segment
if let best = result.bestScore(forSegment: 0) {
print("Best: \(Int(best * 100))%")
}

Static Members

MemberDescription
SingingResult.EMPTYEmpty result constant (score 0, no segments)
SingingResult.calculateOverallScore(segmentResults, aggregation)Calculate overall score from segment results

ResultAggregation

How to aggregate multiple attempts per segment into a final score.

ValueDescription
LATESTUse the most recent attempt's score
BESTUse the highest score across all attempts
AVERAGEUse the average of all attempts

PerformanceLevel

Score-based classification for singing evaluation results.

Values

ValueScore RangeDisplay NameDescription
NEEDS_WORK< 0.3"Needs Work"Significant improvement needed
FAIR0.3 - 0.6"Fair"Room for improvement
GOOD0.6 - 0.8"Good"Solid performance
VERY_GOOD0.8 - 0.95"Very Good"Very strong performance
EXCELLENT>= 0.95"Excellent"Outstanding performance
NOT_EVALUATEDN/A"Not Evaluated"Could not evaluate (insufficient data)
NOT_DETECTED< 0"No Voice"No voice detected during segment

Properties

PropertyTypeDescription
displayNameStringHuman-readable display name for UI

Factory Methods

MethodDescription
PerformanceLevel.fromScore(score)Get level based on score (0.0-1.0, negative for NOT_DETECTED)
PerformanceLevel.fromCode(code)Convert from integer code (for JNI/C interop)

Kotlin

val level = PerformanceLevel.fromScore(0.85f)
// level == PerformanceLevel.VERY_GOOD
println(level.displayName) // "Very Good"

Swift

let level = PerformanceLevel.fromScore(0.85)
// level == .veryGood
print(level.displayName) // "Very Good"

PracticePhase

Practice phase during a CalibraLiveEval session.

Phase Progressions

Singalong: IDLE -> SINGING -> EVALUATED

  • Student sings with the reference audio simultaneously

Singafter: IDLE -> LISTENING -> SINGING -> EVALUATED

  • Student listens to reference first, then sings during their turn
ValueDescription
IDLENot practicing - waiting to start
LISTENINGReference playing, student not recording yet (singafter only)
SINGINGStudent is being recorded and evaluated
EVALUATEDSegment complete, score available

SessionPhase

Current phase of a CalibraLiveEval session.

ValueDescription
IDLESession created but not started
READYReference loaded, ready to begin practicing
PRACTICINGActively capturing and evaluating audio for a segment
BETWEEN_SEGMENTSFinished one segment, waiting before next
COMPLETEDAll segments completed or session manually finished
CANCELLEDSession was cancelled
ERRORAn error occurred

SessionState

Current state of a CalibraLiveEval session. Exposed as a StateFlow from CalibraLiveEval.

Properties

PropertyTypeDescription
phaseSessionPhaseCurrent phase of the session
activeSegmentIndexInt?Index of segment being practiced, or null if none
activeSegmentSegment?The segment being practiced, or null if none
currentPitchFloatCurrent detected pitch in Hz (-1 for unvoiced)
currentAmplitudeFloatCurrent audio amplitude (0.0 - 1.0)
segmentProgressFloatProgress through current segment (0.0 - 1.0)
completedSegmentsSet<Int>Set of segment indices that have been completed
errorString?Error message if phase is ERROR, null otherwise
isPracticingBooleanTrue if session is actively practicing
canBeginSegmentBooleanTrue if session is ready to start or between segments
isFinishedBooleanTrue if session is finished (completed, cancelled, or error)
completedCountIntNumber of completed segments

Static Members

MemberKotlinSwiftDescription
IdleSessionState.IDLE.idleInitial idle state
ReadySessionState.ready()SessionState.ready()Create a ready state
ErrorSessionState.error(message)SessionState.error(message:)Create an error state

ActiveSegmentState

State of the currently active segment during practice.

Properties

PropertyTypeDescription
segmentIndexIntIndex of the segment
segmentSegmentThe segment being practiced
currentPitchFloatCurrent detected pitch in Hz (-1 for unvoiced)
currentAmplitudeFloatCurrent audio amplitude (0.0 - 1.0)
elapsedSecondsFloatTime elapsed since segment started
isCapturingBooleanWhether audio is currently being captured
progressFloatProgress through the segment (0.0 - 1.0)
remainingSecondsFloatTime remaining in seconds
hasVoiceBooleanTrue if detected pitch is valid

SessionConfig

Configuration for a CalibraLiveEval session.

Presets

PresetKotlinSwiftDescription
DefaultSessionConfig.DEFAULT.defaultBalanced, auto-advancing
PracticeSessionConfig.PRACTICE.practiceRepeats until 70% or 3 attempts, best score
KaraokeSessionConfig.KARAOKE.karaokeAlways advances, one attempt
PerformanceSessionConfig.PERFORMANCE.performanceStrict, one attempt, no repetition

Builder

Kotlin

val config = SessionConfig.Builder()
.preset(SessionConfig.PRACTICE)
.scoreThreshold(0.6f)
.maxAttempts(5)
.resultAggregation(ResultAggregation.BEST)
.build()

Swift

let config = SessionConfig.Builder()
.preset(.practice)
.scoreThreshold(0.6)
.maxAttempts(5)
.resultAggregation(.best)
.build()

Config Properties

PropertyTypeDefaultDescription
autoAdvanceBooleantrueAutomatically advance to next segment
scoreThresholdFloat0Min score to auto-advance (0 = disabled)
maxAttemptsInt0Max attempts before forced advance (0 = unlimited)
resultAggregationResultAggregationLATESTHow to aggregate multiple attempts
hopSizeInt160Hop size between frames in samples (160 = 10ms at 16kHz)
autoPhaseTransitionBooleantrueAuto transition LISTENING to SINGING in singafter mode
autoSegmentDetectionBooleantrueAuto detect segment end from player time

Builder Methods

MethodDescription
preset(config)Start from a preset configuration
autoAdvance(enabled)Enable or disable auto-advance
scoreThreshold(threshold)Set minimum score threshold (0 = disabled)
maxAttempts(max)Set maximum attempts (0 = unlimited)
resultAggregation(agg)Set how to aggregate multiple attempts
hopSize(samples)Set hop size between frames
autoPhaseTransition(enabled)Enable or disable auto phase transition
autoSegmentDetection(enabled)Enable or disable auto segment end detection

ScoringAlgorithm

Algorithm for computing note accuracy scores.

ValueDescription
SIMPLESimple threshold counting. Counts percentage of pitch samples within 35 cents of target. Good for beginners.
WEIGHTEDWeighted duration-aware scoring. Tighter thresholds, considers note duration. Good for advanced evaluation.

NoteEvalConfig

Configuration for note evaluation scoring.

Properties

PropertyTypeDefaultDescription
algorithmScoringAlgorithmSIMPLEAlgorithm for computing scores
boundaryToleranceMsInt0Milliseconds to skip at note start/end

Presets

PresetKotlinSwiftAlgorithmBoundary ToleranceDescription
LenientNoteEvalPreset.LENIENT.lenientSIMPLE200msBeginner-friendly
BalancedNoteEvalPreset.BALANCED.balancedSIMPLE100msStandard practice
StrictNoteEvalPreset.STRICT.strictWEIGHTED0msAdvanced/performance

EvaluatorConfig

Configuration for singing evaluation.

Properties

PropertyTypeDefaultDescription
sampleRateInt16000Audio sample rate in Hz
semitoneShiftInt0Transpose reference by this many semitones

Presets

PresetKotlinSwiftSemitone ShiftDescription
StandardEvaluatorPreset.STANDARD.standard0Standard evaluation at original pitch
Male to FemaleEvaluatorPreset.MALE_TO_FEMALE.maleToFemale+12Transpose up one octave
Female to MaleEvaluatorPreset.FEMALE_TO_MALE.femaleToMale-12Transpose down one octave
Transpose UpEvaluatorPreset.TRANSPOSE_UP.transposeUp+2Slight pitch adjustment up
Transpose DownEvaluatorPreset.TRANSPOSE_DOWN.transposeDown-2Slight pitch adjustment down

BreathMetrics

Result of breath analysis containing capacity and control metrics.

PropertyTypeDescription
capacityFloatBreath capacity in seconds - measures longest sustained phrase
controlFloatBreath control score (0.0 to 1.0) - measures breathing pattern consistency
isValidBooleanWhether the result is valid (enough data was available)

Error Types

Kotlin

Calibra uses CalibraException with a CalibraErrorType enum:

Error TypeDescription
INITIALIZATION_FAILEDFailed to initialize native library
INVALID_CONFIGInvalid configuration parameters
ALLOCATION_FAILEDNative resource allocation failed
PROCESSING_ERRORProcessing error
FILE_ERRORFile not found or inaccessible
UNKNOWNUnknown error
try {
val detector = CalibraPitch.createDetector(config)
} catch (e: CalibraException) {
when (e.type) {
CalibraErrorType.INITIALIZATION_FAILED -> println("Init failed: ${e.message}")
CalibraErrorType.INVALID_CONFIG -> println("Bad config: ${e.message}")
else -> println("Error: ${e.message}")
}
}

Swift

Calibra provides typed error enums on iOS for each subsystem. All conform to LocalizedError and provide descriptive errorDescription messages.

PitchDetectorError

CaseParametersDescription
initializationFailedreason: StringFailed to initialize pitch detector
detectionFailedreason: StringPitch detection failed
invalidSampleRaterate: IntInvalid sample rate (expected 16000 Hz)
bufferTooSmallsize: Int, required: IntBuffer too small for detection
do {
let detector = try CalibraPitch.createDetector(config: config)
} catch let error as PitchDetectorError {
print(error.localizedDescription)
}

EvaluatorError

CaseParametersDescription
initializationFailedreason: StringFailed to initialize evaluator
invalidReferencereason: StringInvalid reference audio
evaluationFailedreason: StringEvaluation failed
segmentNotFoundindex: IntSegment not found at given index
captureNotStarted(none)Capture was not started before evaluation
yamlParsingFailedpath: StringFailed to parse YAML file
do {
let session = try CalibraLiveEval.create(reference: material, config: config)
} catch let error as EvaluatorError {
switch error {
case .invalidReference(let reason):
print("Invalid reference: \(reason)")
default:
print(error.localizedDescription)
}
}

EffectsError

CaseParametersDescription
initializationFailedeffect: String, reason: StringFailed to initialize effect
processingFailedeffect: String, reason: StringEffect processing failed
invalidParametername: String, value: Float, range: StringInvalid parameter value

AnalysisError

CaseParametersDescription
insufficientDatarequired: String, actual: StringNot enough data for analysis
detectionFailedtype: String, reason: StringDetection failed
invalidInputparameter: String, reason: StringInvalid input parameter

TimeUtils

Platform-specific current time utility used internally for benchmarking in public APIs.

// Internal API — not directly consumed by SDK users
internal expect fun currentTimeMillis(): Long

currentTimeMillis() is an internal expect/actual function that returns the current time in milliseconds. Each platform provides its own implementation. This is used internally by Calibra APIs for performance timing and benchmarking; it is not part of the public API surface.

Next Steps