Skip to main content

TesseraAgility

Vocal agility scoring — how dynamically the singer varies pitch (fast ornaments, gamakas, runs). Operates on a PitchContour.

Quick Start

Kotlin

val score = TesseraAgility.computeScore(contour)
println("Agility: ${score.scores.first()}")

// Per-segment scoring
val segments = listOf(0f to 5f, 5f to 10f)
val perSegment = TesseraAgility.computeScore(contour, segments = segments)

Swift

let score = TesseraAgility.computeScore(contour: contour)
print("Agility: \(score.scores.first ?? 0)")

Methods

MethodDescription
computeContour(contour, config = AgilityConfig.DEFAULT): AgilityContour10-stage pipeline intermediate (RMS, RMS-oscillation, smoothed pitch, times)
computeScore(agilityContour, segments = null, config = DEFAULT): AgilityScoreScore from a pre-computed contour
computeScore(contour, segments = null, config = DEFAULT): AgilityScoreOne-shot from a PitchContour

segments: List<Pair<Float, Float>>?. When null, the score is the mean of the top 50% of L-second windows with L/2 overlap. When provided, returns one score per segment.

Result types

AgilityScore

data class AgilityScore(val scores: FloatArray)

Each score is in [0, 1]. Values below 0.5 indicate little to no vocal agility.

AgilityContour

data class AgilityContour(
val rms: FloatArray, // log-derivative RMS of pitch
val rmsOsc: FloatArray, // oscillation RMS with boundary cleaning
val pitchSmooth: FloatArray, // pitch in MIDI after smoothing + OneEuro
val times: FloatArray,
)

AgilityConfig

Currently only one preset (DEFAULT); parameters tuned for Indian classical music.

PropertyTypeDefault
featureRateFloat10
tauFloat3
logKFloat0.6
logMFloat4.8
classifierAFloat5
classifierBFloat18
classifierCFloat10.5
sigmoidAlphaFloat0.6
windowSecondsFloat8

Builder mirrors the field names.

Common Pitfalls

  1. Contour must have ≥ 2 samples (throws per ADR-022).
  2. Segment times are in seconds. A Pair(startSec, endSec). Zero/negative-duration segments return an explicit 0.5 fallback.
  3. Segments with no data points score near 0, not 0.5 — only the explicit-fallback case returns 0.5.

See also