Kodio 0.2.0 Help

Migration

Guide for upgrading between Kodio versions.

0.1.x to 0.2.0

Compose API consolidation

RecordAudioState/rememberRecordAudioState() and PlayAudioState/rememberPlayAudioState() were removed, along with KodioTheme, KodioComponents, AudioGraph, KodioIcons, and KodioColors. Use RecorderState/PlayerState and AudioWaveform instead.

val recordState = rememberRecordAudioState() KodioTheme { KodioComponents.RecordButton(state = recordState) AudioGraph(amplitudes = recordState.amplitudes) }
val recorderState = rememberRecorderState() Button( onClick = { recorderState.toggle() }, enabled = !recorderState.isProcessing, ) { Text(if (recorderState.isRecording) "Stop" else "Record") } if (recorderState.isRecording) { AudioWaveform(amplitudes = recorderState.liveAmplitudes) } recorderState.recording?.let { recording -> Button(onClick = { recording.play() }) { Text("Play Recording") } }

Material 3 components

RecordAudioButton, PlayAudioButton, ErrorDialog, and AudioPermissionButton now take RecorderState or PlayerState instead of the removed Compose state types.

val recordState = rememberRecordAudioState() RecordAudioButton( state = recordState, onRecordClick = { recordState.start() }, ) ErrorDialog(message = recordState.errorMessage, onDismiss = { recordState.clearError() })
val recorderState = rememberRecorderState() RecordAudioButton(state = recorderState) recorderState.error?.let { error -> ErrorDialog(error = error, onDismiss = { recorderState.clearError() }) } val playerState = rememberPlayerState(recording) PlayAudioButton(recording = recording, state = playerState) AudioPermissionButton(state = recorderState)

Transcription configuration

TranscriptionConfig now has only the language field. Removed fields: model, interimResults, punctuation, profanityFilter, diarization, and keywords. Removed presets: VoiceCommand and Meeting. Removed type: TranscriptionException.

audioFlow.transcribe( engine, TranscriptionConfig.VoiceCommand.copy(language = "en-US"), )
audioFlow.transcribe( engine, TranscriptionConfig(language = "en-US"), // or TranscriptionConfig.Default )

Errors from transcription now surface as TranscriptionResult.Error in the result flow, not as thrown TranscriptionException.

JVM native path on macOS

The macOS JVM native CoreAudio path now requires JDK 22 or later (Panama FFI finalized in JDK 22). On JDK 24 and later, pass --enable-native-access=ALL-UNNAMED to silence restricted native access warnings.

See Platform Setup for JVM flags and Gradle configuration.

AudioRecording equality

AudioRecording.equals() now compares audio content (byte-for-byte), not just format and size. Two recordings with the same format and size but different samples are no longer equal. hashCode() remains coarse (format and size only), so do not use AudioRecording as a hash map key when content equality matters.

Recording start failures

In 0.2.0, Recorder.start() throws when the underlying recording session fails to start (for example a hardware error or an unsupported format that cannot be negotiated). Previously, some failures left the session in an error state without propagating to the caller. If you call Recorder.start() directly, wrap it in try/catch. In Compose, RecorderState.start() catches these exceptions and surfaces them via RecorderState.error.

0.0.5 to 0.0.6

Error types are now classes for proper stack traces:

when (error) { AudioError.PermissionDenied -> ... AudioError.NotInitialized -> ... }
when (error) { is AudioError.PermissionDenied -> ... is AudioError.NotInitialized -> ... }

Session API to high-level API

val session = SystemAudioSystem.createRecordingSession() session.start() delay(5000) session.stop() val audioFlow = session.audioFlow.value!!
val recording = Kodio.record(duration = 5.seconds) recording.play()

Compose migration

var session by remember { ... } var isRecording by remember { ... } DisposableEffect(Unit) { ... }
val recorderState = rememberRecorderState() Button(onClick = { recorderState.toggle() }) { ... }

Accessing sessions

The session API is still available for advanced use:

@Suppress("DEPRECATION") val session = recorder.underlyingSession()
Last modified: 06 July 2026