A pure-native Android app that plays and creates Codec 2 — the world's smallest open-source voice codec — in about ~300 KB installed.
No third-party libraries (no AndroidX, no Kotlin, no Material) — just core android.* plus a single small JNI .so. Single ABI (armeabi-v7a). The whole app installs in roughly the size of a couple of screenshots.
The app ships with English and Turkish, switchable in Settings → Language (independent of the device locale). This README is in English.
Codec 2 (by David Rowe, LGPL) compresses human speech to 450 – 3200 bits per second. How small is that? In this app, a 3-minute song shrinks to ~14 KB in the 450 mode. It's built for radio/satellite/low-bandwidth voice and tiny voice archives.
Playback
- Multi-file playlist; tap to play, auto-advance
- One glowing play/pause button (pulse + circular progress ring)
- Touch-and-drag waveform scrubber, elapsed / total time
- ⏮ ⏭ prev/next; long-press to seek ±10 s; "prev" restarts the track after 3 s
- Shuffle; 3-state repeat (off / all / one)
- Playback speed 0.75×–2×
- Audio gain (dB) — unlimited, can go negative, free-form value
- Sleep timer (15/30/60 min)
- Resume where you left off (track + position persisted)
- Mode is auto-detected from the file header (3200…450); colour-coded badges
- Plays normal audio too —
mp3/ogg/m4a/aac/wav/flacvia the device's ownMediaPlayer(true streaming, any length);.c2files use the built-in codec2 decoder. Routed by file header, so a huge song never gets force-decoded into RAM. Handy to A/B a track against its codec2 version.
Background & notification
- Foreground Service + MediaSession (lock screen / headset buttons)
- Notification prev/play-pause/next; notification hides when app is foregrounded
- Battery: wakelock only while playing; auto-pause when headphones are unplugged
Files & conversion
- Add files / multiple files via SAF; recursively scan a folder (no permission needed)
- Open
.c2files from outside (single & multiple intent-filters) → added to the list and played - Convert any audio to
.c2(mp3/aac/m4a/opus/ogg/wav/flac…) using the device's own codecs (MediaCodec) — no external library. Pick a size↔quality mode; the output is written next to the original (auto-numbered on collision) - Share as WAV: decodes
.c2back to a normal WAV and shares it (tiny built-in ContentProvider) - Reorder the list + long-press menu (no delete); Info dialog shows file size
- Live Log / Console screen (prints conversion steps)
Design — animated gradient background, glowing/pulsing controls, smooth blue↔pink colour shift, accent stripe on the playing row, marquee for long names, press-scale animation, mode-coloured labels — all library-free, pure canvas/animator.
| Part | Size |
|---|---|
libcodec2player.so (armeabi-v7a, encode+decode) |
~263 KB |
classes.dex (the whole app) |
~10 KB |
| res + manifest + signature | ~3 KB |
| Total ≈ installed size | ~300 KB |
- Only the Codec 2 voice core is compiled; the FreeDV radio modems (OFDM/COHPSK/FSK/FDMDV)
and the LDPC tables (
H_*.c, megabytes) are left out. Encode is essentially free on top of decode (decode-only.so268,544 B → encode+decode 268,800 B). extractNativeLibs=false→ the.sois mmap'd from the APK, not copied on install.android:debuggable="true"→run-from-apk; the system skipsdex2oat/oat, so installed size ≈ APK size. (Personal-use trick; not recommended for store distribution.)- Single ABI, R8 +
shrinkResources, v1-only EC signature.
It's a standard Android project (AGP 8.0.2, compileSdk 33, minSdk 21):
./gradlew assembleReleaseIt was developed with a portable, offline toolchain (bundled JDK + SDK + Gradle) driven by a
Derle.ps1 script that also runs zipalign -p + apksigner (v1-only, EC) and applies the
minimal-installed-size settings above.
libcodec2player.so is the voice-only subset of Codec 2
1.0.x plus the JNI bridge in src/main/cpp/Codec2JNI.c, compiled with NDK clang (single ABI,
-O2, --gc-sections) and stripped. Codebook .c files are generated from .txt
(native/gen_cb.py). The steps live in native/build_jni.ps1. (The Codec 2 source itself comes
from upstream; this repo ships the prebuilt .so.)
The UI is fully localised and the language can be switched in-app (Settings → Language:
System / English / Türkçe), independent of the device locale. Strings live in
res/values/strings.xml (English, default) and res/values-tr/strings.xml (Turkish). To add a
language, copy strings.xml into res/values-<lang>/, translate the values, and add it to the
picker in SettingsActivity. PRs welcome.
- Application source (the original code in this repo): MIT — see
LICENSE. - The bundled
libcodec2player.sois derived from Codec 2 and is licensed under LGPL-2.1, © David Rowe and contributors — https://github.com/drowe67/codec2.
- David Rowe and the Codec 2 team — the codec itself.
- The
.c2file header (C0 DE C2 …) and the JNI approach were referenced from the Codec2Recorder project.


