- upload ran as a detached coroutine while cleanup() stopped the
service, and onDestroy's scope.cancel() killed it before any status
appeared; the upload is now part of finishRecording itself and the
service stays foreground ("Saving recording…") until it completes
- JNI getTextSegment used NewStringUTF, which ABORTS THE PROCESS on
invalid UTF-8 — whisper can emit garbled bytes on noisy audio (seen
live: SIGABRT with illegal start byte 0x8d); whisper_jni.cpp now
decodes UTF-8 to UTF-16 itself, replacing bad sequences with U+FFFD
- validated on device end-to-end: record -> local final pass -> automatic
upload -> library entry with all four files (verified server-side)
- new Transcriber interface makes the recorder service engine-agnostic;
WhisperEngine (local JNI) and new RemoteWhisperEngine (POST /inference,
verbose_json, in-memory WAV upload via new WavEncoder) implement it
- SessionConfig carries serverUrl; when set, BOTH the live pass and the
final pass transcribe remotely (server owns the model, e.g. large-v3
on GPU); local engine remains the offline fallback
- UI: Transcribe on: phone/server dropdown + server URL field,
persisted in SharedPreferences; model controls grey out in server
mode; manual Transcribe also routes to the server
- network security config permits cleartext HTTP for user-configured
LAN/tailnet servers (documented; HTTPS works either way)
- WavEncoder round-trip unit test (11 total green); validated phone ->
Tailscale -> R9700 with large-v3 before the app-side change
- RecorderService: rolling-window live pass with a dedicated live model
(off/tiny/base, beam 1, 8 s ticks, time-based dedupe) and an automatic
final pass with the selected model (beam 5) writing txt/srt/json
- TranscriptFiles in core/whisper: desktop-compatible outputs, 5 JVM tests
- WhisperEngine exposes modelName; UI: live-model dropdown, merged
transcript view, share sheet; falls back to manual path without engine
- live loop failures now logged (MeetRec tag) and surfaced in the UI
(was silently swallowed), plus final-pass timing logs
On-device measurements (Fairphone 6): tiny live ~0.6x realtime, small
final ~0.8x realtime — motivates the planned whisper-server engine.