Skip to content
Klarinet 0.6.0 Moves the Shared DSP Core to Rust

Klarinet 0.6.0 Moves the Shared DSP Core to RustEl núcleo DSP de Klarinet 0.6.0 ya es Rust

Rust & NativeRust y nativo

ByPor David Cruz Anaya, Senior Mobile & Applied AI Engineer (Android, Kotlin Multiplatform, product AI)Ingeniero sénior de móvil e IA aplicada (Android, Kotlin Multiplatform, IA de producto)

Updated 8 November 2026Actualizado el 8 de noviembre de 20264 min read4 min de lectura
RustKotlin MultiplatformAudioDSPOpen SourceAndroid
Share:Compartir:

In Part 10 of the Rust on Android series I wrote that if I started Klarinet’s engine today, the DSP core would be Rust. Klarinet 0.6.0, published 7 November 2026, is that core.

The published Kotlin API stays. Queues, the effect chain, the sixteen effects, and the callback offload now live in the shared Rust DSP. The audio callback still does not allocate, lock, or enter Kotlin. Vendored Oboe and miniaudio stay wrapped.

The Klarinet introduction still describes 0.0.1, when that core was C++.

Install

implementation("com.vectencia.klarinet:klarinet:0.6.0")
implementation("com.vectencia.klarinet:klarinet-coroutines:0.6.0")

The Android variant of klarinet already depends on com.vectencia.klarinet:klarinet-jni:0.6.0. The Klarinet case study still shows the 0.0.1 coordinates.

Where that core is linked

JVM, Android (arm64-v8a, armeabi-v7a, x86_64), linuxX64, and every Gradle Apple DSP archive link it.

On the iOS, tvOS, and watchOS demos, the sine callbacks fill through klarinet_render_sine.

Analysis, WAV, and the browser

Fft, AudioAnalyzer, AudioMath, and LinearResampler run in Rust on JVM, Android, JS, and Apple. Native desktop keeps the Kotlin body for those four.

JS and JVM encode and decode WAV through one Rust codec. Native desktop keeps miniaudio. Android keeps MediaCodec. Apple keeps ExtAudioFile. Compressed decode on JS stays decodeAudioData.

On JS, Gain runs in the wasm worklet and uses AudioMath.dbToLinear. The other effect types stay Web Audio nodes. SampleRing stays the main-thread queue. A change to FADE_MS or enable does not restart a gain ramp already in progress.

Android routes

The Android native backend uses Google Oboe 1.11.0. Data, error, and routing callbacks are shared_ptrs, registered with setDataCallback, setErrorCallback, and setRoutingCallback.

observeRouteChanges and routeChangeFlow emit reason = "RoutingChanged" when Oboe reports a routing change on API 37 and newer. previousRoute is the previous device id list, or unspecified. On older Android versions the stream still disconnects, and these calls do not emit.

Demos

The Compose, web, and SwiftUI demos picked up requestRecordPermission, routeChangeFlow / clearRouteChanges, AnalyzingCallback, asFlow, band-pass bandwidth in octaves, and underrun counts. They also have a studio visual system with animated screen transitions.

:sample has a perm listen path for hasRecordPermission and requestRecordPermission.

Spirit Box

Spirit Box is a Compose Multiplatform app in demos/spirit-box. It depends on klarinet:0.6.0 and klarinet-coroutines:0.6.0. Android, iOS, and the JVM desktop share the session code. It landed on main on 8 November 2026. The v0.6.0 tag does not include it.

It hops twelve bundled clips, six FM and six AM, mixed with noise, so short bits of speech come through the static. Dwell is 50 to 350 ms. The sweep runs forward, reverse, or held. One band-pass sits at 1 kHz, 1 octave on FM and 2.5 on AM. The band switch calls setParameter on BPFParams.BANDWIDTH while the output stream is running, then a gain stage. A second AudioEngine opens the microphone and the session mixes it in. AudioFileWriter stores the dry mix and the station clip on the session coroutine. The audio callback only copies into a ring. There is no tap after the effect chain, so the WAV is the mix from before the band-pass and the live output is the stream after it.

Review loads the take with AudioFileReader and plays it on another low-latency output stream, with rate, reverse, and gain on that stream. The scan stays in the voice band, 300 Hz to 3400 Hz, and marks a span of 80 ms to 2 s when it clears the noise floor and the station clip does not account for it.

The hop clock, the noise mix, and the review cursor stay in Kotlin. The band-pass and the gain run in the Rust DSP on the audio thread. That callback still does not allocate, lock, or enter Kotlin.

Publishing and the docs

Maven publish builds :klarinet:jsSourcesJar after generateKlarinetWasm, so the generated wasm source lands in the jar. GitHub Pages is assembled by scripts/assemble-pages.sh, which the workflow and make pages both call.

The maintainer guide is GITHUB_PAGES.md. It covers the published tree, how to enable Pages, the workflow, a local preview with make pages, CORS and the microphone, and troubleshooting. README and CONTRIBUTING point the live demo and /api/ at that guide. The README install samples use 0.6.0. Dokka for Klarinet and Klarinet Coroutines includes the coordinates, a playback and effects implementation, and the host steps for Android, Apple, JVM, and the browser.

Web demo. API docs.

En la parte 10 de la serie Rust en Android escribí que, si empezara hoy el motor de Klarinet, el núcleo DSP sería Rust. Klarinet 0.6.0, publicado el 7 de noviembre de 2026, es ese núcleo.

La API de Kotlin publicada se queda. Las colas, la cadena de efectos, los dieciséis efectos y la descarga de trabajo fuera del callback viven ahora en el DSP compartido en Rust. El callback de audio sigue sin asignar memoria, sin tomar cerrojos y sin entrar en Kotlin. Oboe y miniaudio, vendorizados, siguen envueltos.

La presentación de Klarinet sigue describiendo la 0.0.1, cuando ese núcleo era C++.

Instalación

implementation("com.vectencia.klarinet:klarinet:0.6.0")
implementation("com.vectencia.klarinet:klarinet-coroutines:0.6.0")

La variante de Android de klarinet ya depende de com.vectencia.klarinet:klarinet-jni:0.6.0. El caso de Klarinet sigue mostrando las coordenadas de la 0.0.1.

Dónde se enlaza ese núcleo

JVM, Android (arm64-v8a, armeabi-v7a, x86_64), linuxX64 y cada artefacto DSP de Apple que produce Gradle enlazan ese núcleo.

En las demos de iOS, tvOS y watchOS, los callbacks del seno se rellenan con klarinet_render_sine.

Análisis, WAV y el navegador

Fft, AudioAnalyzer, AudioMath y LinearResampler corren en Rust en JVM, Android, JS y Apple. El desktop nativo conserva el cuerpo en Kotlin de esos cuatro.

JS y JVM codifican y descodifican WAV con un único códec en Rust. El desktop nativo conserva miniaudio. Android conserva MediaCodec. Apple conserva ExtAudioFile. La descodificación comprimida en JS sigue siendo decodeAudioData.

En JS, Gain corre en el worklet de wasm y usa AudioMath.dbToLinear. Los demás tipos de efecto siguen siendo nodos de Web Audio. SampleRing sigue siendo la cola del hilo principal. Un cambio de FADE_MS o de enable no reinicia una rampa de ganancia que ya está en curso.

Rutas en Android

El backend nativo de Android usa Google Oboe 1.11.0. Los callbacks de datos, de error y de ruta son shared_ptr, y se registran con setDataCallback, setErrorCallback y setRoutingCallback.

observeRouteChanges y routeChangeFlow emiten reason = "RoutingChanged" cuando Oboe informa de un cambio de ruta en la API 37 o superior. previousRoute es la lista anterior de ids de dispositivo, o unspecified. En versiones anteriores de Android el flujo se desconecta igual, y estas llamadas no emiten.

Demos

Las demos de Compose, web y SwiftUI traen requestRecordPermission, routeChangeFlow / clearRouteChanges, AnalyzingCallback, asFlow, el ancho de banda del paso de banda en octavas y la cuenta de underruns. También tienen un sistema visual de estudio, con transiciones de pantalla animadas.

:sample tiene una ruta de escucha perm para hasRecordPermission y requestRecordPermission.

Spirit Box

Spirit Box es una app de Compose Multiplatform en demos/spirit-box. Depende de klarinet:0.6.0 y klarinet-coroutines:0.6.0. Android, iOS y el escritorio JVM comparten el código de la sesión. Entró en main el 8 de noviembre de 2026. La etiqueta v0.6.0 no la incluye.

Salta entre doce clips, seis de FM y seis de AM, mezclados con ruido, para que salgan fragmentos cortos de voz entre la estática. El tiempo en cada emisora va de 50 a 350 ms. El barrido va hacia delante, hacia atrás, o se queda fijo. Un paso de banda a 1 kHz, 1 octava en FM y 2,5 en AM. El cambio de banda llama a setParameter sobre BPFParams.BANDWIDTH con el flujo de salida en marcha, y después una etapa de ganancia. Un segundo AudioEngine abre el micrófono y la sesión lo mezcla. AudioFileWriter guarda la mezcla en seco y el clip de la emisora en la corrutina de la sesión. El callback de audio solo copia a un anillo. No hay toma después de la cadena, así que el WAV es la mezcla de antes del paso de banda y la salida en directo es el flujo de después.

La revisión carga la toma con AudioFileReader y la reproduce en otro flujo de salida de baja latencia, con velocidad, sentido y ganancia en ese flujo. El análisis se queda en la banda de voz, de 300 Hz a 3400 Hz, y marca un tramo de 80 ms a 2 s cuando supera el suelo de ruido y el clip de la emisora no lo explica.

El reloj del salto, la mezcla con el ruido y el cursor de la revisión se quedan en Kotlin. El paso de banda y la ganancia corren en el DSP de Rust, en el hilo de audio. Ese callback sigue sin asignar, sin cerrojos y sin entrar en Kotlin.

Publicación y documentación

La publicación en Maven construye :klarinet:jsSourcesJar después de generateKlarinetWasm, así que el código fuente wasm generado entra en el jar. GitHub Pages se monta con scripts/assemble-pages.sh, que llaman tanto el workflow como make pages.

La guía de mantenimiento es GITHUB_PAGES.md. Cubre el árbol publicado, cómo activar Pages, el workflow, una vista previa local con make pages, CORS y el micrófono, y los fallos habituales. README y CONTRIBUTING apuntan la demo en vivo y /api/ a esa guía. Los ejemplos de instalación del README usan la 0.6.0. Dokka de Klarinet y de Klarinet Coroutines incluye las coordenadas, una implementación de reproducción y de efectos, y los pasos del host para Android, Apple, JVM y el navegador.

Demo web. Documentación de la API.

Share:Compartir:

CommentsComentarios

Loading comments…Cargando comentarios…