#226·moonshine

MicTranscriber: optional live audio-level callback across SDKs (Android use case)

Author: sanogueralorenzoCreated Sep 1, 2026Updated Sep 2, 2026

Use case

I am building an Android voice keyboard with a waveform that should react as soon as the user speaks.

MicTranscriber.onText and onLine are intentionally downstream of VAD/transcription work, so they arrive too late to drive responsive microphone UI. Opening a second AudioRecord only for a visualizer introduces concurrent-capture and device-compatibility risks.

With ai.moonshine:moonshine-voice:0.1.5, the only low-latency workaround I found is reflecting into the private captureLock / micCaptureProcessor fields and wrapping MicCaptureProcessor.consumeAudio() to calculate RMS from the same samples Moonshine consumes.

Request

Could MicTranscriber expose an optional callback based on its existing microphone samples? On Android, for example:

kotlin
val mic = MicTranscriber(context)
    .onAudioLevel { level ->
        updateVisualizer(level.rms, level.peak)
    }

The exact API is flexible. A normalized level or an object containing RMS/peak would be sufficient; raw PCM is not required for this use case.

Android is the immediate use case, but ideally this should be part of the common MicTranscriber contract across every supported SDK rather than an Android-specific extension. Equivalent APIs could follow each binding's naming conventions:

python
mic.on_audio_level(update_visualizer)
swift
mic.onAudioLevel { level in
    updateVisualizer(level.rms, level.peak)
}
javascript
mic.onAudioLevel(level => {
    updateVisualizer(level.rms, level.peak)
})

Useful behavior across SDKs would be:

  • Available on every MicTranscriber implementation.
  • Disabled by default, with no level-calculation overhead when unset.
  • Calculated from the existing capture stream rather than a second recorder.
  • A consistent value range and payload, such as normalized RMS and peak.
  • Callback cadence documented and reasonably bounded for UI use.
  • Callback threading follows callbacksOnMainThread(...) on Android, with the equivalent behavior documented for other platforms.
  • Callbacks occur only for the active recording lifecycle and stop cleanly with stop() / close().

This would support waveform visualizers, VU meters, silence indicators, and push-to-talk interfaces while keeping microphone ownership inside Moonshine and allowing the same UI architecture across platforms.