Android M2: live rolling transcript and automatic final pass

- 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.
This commit is contained in:
2026-09-07 12:14:24 +02:00
parent accfe15350
commit 46b0f128bc
7 changed files with 465 additions and 48 deletions
+17 -11
View File
@@ -4,10 +4,10 @@ Native Android app with the same functionality as the desktop meetrec:
record meetings and transcribe them on-device with Whisper. Nothing leaves
the phone — no cloud, no telemetry.
Status: **M1 (recording)** — record a meeting with a foreground service
(native sample rate, timer, level meter), then transcribe it on device.
Model download + engine load are from M0; the live rolling transcript
arrives with M2 (see the milestone plan in the project docs).
Status: **M2 (live transcript + automatic final pass)** — record a meeting
and watch the live rolling transcript while recording; on Stop the final
(better) pass runs automatically and saves `.txt`/`.srt`/`.json` next to
the WAV, mirroring the desktop two-pass design.
## Requirements
@@ -33,10 +33,16 @@ release tag so the JNI layer never breaks on upstream churn.
1. Launch **MeetRec**, pick a model (`tiny` is fine for a first test) and tap
**Download** (model comes from Hugging Face; tiny is ~75 MB).
2. Tap **Load engine**.
3. Tap **Record**, speak, then tap **Stop** — the recording is saved as WAV
(native device rate) and auto-loaded for transcription.
4. Tap **Transcribe** — segments with timestamps appear; share via the share
sheet. You can also pick any PCM WAV file instead of recording.
3. Tap **Record** — live transcript lines appear every few seconds (they use
the cheap *live model*, `tiny`/`base`, on a 60 s rolling window).
4. Tap **Stop** — the final pass runs automatically with the selected model
and writes `<stem>.txt/.srt/.json` next to the WAV.
5. Share the transcript via the share sheet. You can also pick any PCM WAV
file and transcribe it manually.
Tip: use `tiny`/`base` as the live model (they keep up on a phone CPU) and
a larger model like `small` for the final pass. The **Live model** dropdown
also has `off` to disable live transcription (longest battery life).
Expect roughly realtime transcription with `tiny`/`base` on the
Fairphone 6's CPU; `small` is noticeably slower — use it for final passes
@@ -51,11 +57,11 @@ only (the live/final split comes with the recorder milestones).
## Module layout
```
app/ Compose UI + RecorderService (foreground mic recording)
app/ Compose UI + RecorderService (record → live pass → final pass)
core/recording/ MeetingRecorder, WavWriter/WavReader, RollingWindow,
linear resampler — pure Kotlin, unit tested on the JVM
core/whisper/ whisper.cpp JNI wrapper: CMake build + LibWhisper.kt +
WhisperEngine.kt (the on-device transcription API)
core/whisper/ whisper.cpp JNI wrapper + WhisperEngine + TranscriptFiles
(txt/srt/json writers, desktop-compatible formats)
tools/ fetch-whisper.sh — vendor the pinned whisper.cpp release
```