8e079467fa
- 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
69 lines
2.8 KiB
Markdown
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). |