Skip to main content

Utilities

Helper classes for parsing music data files, converting between frame indices and time, and error handling.

SonixParser​

Parses pitch, note transcription, and transcription data from text files used in music education.

Methods​

MethodInputReturnsDescription
parsePitchStringcontent: StringPitchData?Parse .pitchPP file content
parseNotesStringcontent: StringNotesData?Parse .notes file content
parseTransStringcontent: StringTransData?Parse .trans JSON file content
parseHpcpbytes: ByteArrayHpcpData?Decode binary HPCP chroma bytes; null if the bytes are malformed (bad magic, unsupported version, or truncated)

Parsing Pitch Data​

Pitch files (.pitchPP) contain time-pitch pairs:

0.0 440.0
0.01 442.5
0.02 -1.0

Kotlin​

val content = File("melody.pitchPP").readText()
val pitchData = SonixParser.parsePitchString(content)
if (pitchData != null) {
for (i in 0 until pitchData.count) {
println("Time: ${pitchData.times[i]}, Pitch: ${pitchData.pitchesHz[i]}")
}
}

Swift​

let content = try String(contentsOf: pitchFileURL)
if let pitchData = SonixParser.parsePitchString(content: content) {
print("Found \(pitchData.count) pitch points")
}

Parsing Notes Data​

Notes files (.notes) contain tab-separated note transcriptions:

4.899410	5.224489	138.591315	Ṇ
4.980680 7.047256 145.986691 S
val notesData = SonixParser.parseNotesString(content)
if let notesData = SonixParser.parseNotesString(content: content) {
print("Found \(notesData.count) notes")
}

Parsing Transcription Data​

Transcription files (.trans) use JSON format with segments:

[{"id": 3, "lyrics": "S... R...", "time_stamp": [3.18, 14.18], "trans": [...]}]
val transData = SonixParser.parseTransString(jsonContent)
if let transData = SonixParser.parseTransString(content: jsonContent) {
print("Found \(transData.segments.count) segments")
}

Data Types​

PitchData​

PropertyTypeDescription
timesFloatArrayTimestamps in seconds
pitchesHzFloatArrayPitch values in Hz (-1.0 = unvoiced)
countIntNumber of data points

NotesData​

PropertyTypeDescription
startTimesFloatArrayNote start times in seconds
endTimesFloatArrayNote end times in seconds
frequenciesFloatArrayNote frequencies in Hz
labelsArray<String>Note labels

TransData​

PropertyTypeDescription
segmentsList<TransSegment>Transcription segments
countIntNumber of segments (segments.size)
MethodReturnsDescription
toNotesData()NotesDataFlatten all segments' notes into a single NotesData

TransSegment​

PropertyTypeDescription
idIntSegment identifier from the JSON
typeString?Raw segment-type string from the JSON type field, deserialized verbatim (e.g. "teacher_vocal", "student_vocal", "commentary"); null when the field is absent. The SDK does not validate or interpret the value.
lyricsStringLyrics for the segment
relatedSegInt?Paired segment's id for singafter lessons (parsed from related_seg); null for singalong. A teacher_vocal and student_vocal pair cross-link via this field.
timeStampList<Double>Two-element segment time bounds (parsed from time_stamp)
transList<TransNote>Per-note transcription within the segment
startTimeDoubleFirst element of timeStamp (timeStamp.getOrNull(0), else 0.0)
endTimeDoubleSecond element of timeStamp (timeStamp.getOrNull(1), else 0.0)

TransNote​

PropertyTypeDescription
tStartDoubleNote start (seconds; parsed from t_start)
tEndDoubleNote end (seconds; parsed from t_end)
freqHzDoubleFrequency in Hz
labelStringNote label

SonixWriter​

Formats reference data into the on-disk text formats parsed by SonixParser — the inverse direction, for tools that author reference lessons.

Methods​

MethodInputReturnsDescription
formatPitchStringdata: PitchDataStringFormat .pitchPP text (one time<TAB>pitchHz line per frame); unvoiced frames written as 0.0. Inverse of parsePitchString.
formatHpcpframes: List<FloatArray>, hopSize: Int, sampleRate: IntByteArrayEncode HPCP chroma frames as binary HPCP bytes. Inverse of parseHpcp.
formatTransStringdata: TransDataStringFormat .trans JSON (array of phrase objects). Inverse of parseTransString.

Usage​

// Round-trips through SonixParser
val text = SonixWriter.formatPitchString(pitchData)
val roundTripped = SonixParser.parsePitchString(text) // voiced times/pitches preserved

val hpcpBytes = SonixWriter.formatHpcp(frames, hopSize = 512, sampleRate = 44100)
val transJson = SonixWriter.formatTransString(transData)
let text = SonixWriter.formatPitchString(data: pitchData)
let hpcpBytes = SonixWriter.formatHpcp(frames: frames, hopSize: 512, sampleRate: 44100)
let transJson = SonixWriter.formatTransString(data: transData)

SonixFrames​

Convert between audio frame indices and time values. Useful for aligning analysis results with audio timestamps.

Methods​

MethodParametersReturnsDescription
framesToTimeframe: Int, sampleRate: Int, hopLength: IntDoubleFrame index to seconds
timeToFramestime: Double, sampleRate: Int, hopLength: IntIntSeconds to frame index
framesToTimeframes: List<Int>, ...List<Double>Batch frame-to-time
timeToFramestimes: List<Double>, ...List<Int>Batch time-to-frame

Usage​

// Convert frame index to time
val timeSeconds = SonixFrames.framesToTime(
frame = 100,
sampleRate = 16000,
hopLength = 160
)

// Convert time to frame index
val frameIndex = SonixFrames.timeToFrames(
time = 1.5,
sampleRate = 16000,
hopLength = 160
)

// Batch conversion
val times = SonixFrames.framesToTime(
frames = listOf(0, 10, 20, 30),
sampleRate = 16000,
hopLength = 160
)

Parameters​

ParameterDescriptionTypical Values
sampleRateAudio sample rate in Hz16000, 44100
hopLengthHop length in samples160, 512

SonixError​

Error information reported by Sonix APIs via StateFlows. Used instead of throwing exceptions for reactive error handling.

Properties​

PropertyTypeDescription
messageStringHuman-readable error description
causeThrowable?Original exception (if available)

Exceptions​

SonixException is a typed exception thrown for setup errors that cannot be modeled as state-flow errors (e.g., decoding a corrupt file). SonixKilledException is a typealias for VoxaTraceKilledException and is thrown by factory methods when licensing fails (see Authentication).

Usage​

player.error.collect { error ->
error?.let {
println("Error: ${it.message}")
it.cause?.let { cause -> cause.printStackTrace() }
}
}
let task = player.observeError { error in
if let error = error {
print("Error: \(error.message)")
}
}

Next Steps​