Files
meetrec/android/README.md
T
fegger 8e079467fa Android M1: meeting recording via foreground service
- core/recording: MeetingRecorder (AudioRecord at the device's native
  rate, WAV on disk + 60 s rolling window resampled to 16 kHz), streaming
  WavWriter, thread-safe RollingWindow, linear resampler; WavReader moved
  here from the app
- app: RecorderService (foreground, type microphone) with ongoing
  notification and StateFlow state; Record/Stop UI with timer and level
  meter; runtime permission flow; finished recordings auto-load for
  transcription
- launcher icon (mic + waveform, matching the desktop brand) and
  notification glyph
- fix real M0 bug caught by the new unit tests: WavReader parsed 16-bit
  fmt fields (audioFormat/channels/bitsPerSample) with 32-bit reads
- JVM tests: WAV round trip, native-rate header, resampler, rolling
  window — 5/5 green; assembleDebug and aapt2 APK checks pass
- validated on-device on a Fairphone 6 (Android 16): model download,
  engine load, recording and transcription all working
2026-09-07 11:31:29 +02:00

69 lines
2.8 KiB
Markdown

# MeetRec for Android
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).
## Requirements
- Android Studio (or: SDK Platform 36, Build Tools 36, NDK 27.1, CMake 3.22.1)
- JDK 17+
- Device running Android 10+ (developed against a Fairphone 6 / Snapdragon
7s Gen 3, Android 15+)
## Build
```sh
cd android
./tools/fetch-whisper.sh # vendors whisper.cpp v1.9.3 into third_party/
./gradlew :app:assembleDebug # or open the android/ folder in Android Studio
adb install -r app/build/outputs/apk/debug/app-debug.apk
```
`third_party/whisper.cpp` is gitignored — the fetch script pins the exact
release tag so the JNI layer never breaks on upstream churn.
## Test on device
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.
Expect roughly realtime transcription with `tiny`/`base` on the
Fairphone 6's CPU; `small` is noticeably slower — use it for final passes
only (the live/final split comes with the recorder milestones).
## Performance notes
- The engine runs on CPU via NEON, using up to 4 threads.
- GPU/NPU acceleration (e.g. Snapdragon NPU via the QNN backend) is a
stretch goal, not wired up yet.
## Module layout
```
app/ Compose UI + RecorderService (foreground mic recording)
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)
tools/ fetch-whisper.sh — vendor the pinned whisper.cpp release
```
Recording mirrors the desktop app: audio is captured at the input's native
rate (WAV on disk keeps it), while the 60 s rolling window is resampled to
Whisper's 16 kHz for the upcoming live pass. Recording continues while the
app is backgrounded via the microphone foreground service.
Models live in the app's private storage (`filesDir/models`), shared files
with the desktop app's `~/.cache/meetrec/whisper-cpp` naming
(ggml-tiny.bin … ggml-large-v3.bin).