Hook the audio path directly inside target apps — transform your voice in calls, recordings, and games with a low-latency native DSP engine, per-app profiles, and a rich companion app.
Actual app entry screen captured from the emulator; the full gallery is in the docs.
[!DANGER]
⚠️ Echidna is experimental root software and may be incompatible with the phone you are using. Do not install it unless you already know how to disable a Magisk/Zygisk module manually from recovery, adb, safe mode, or another out-of-band rescue path if the phone bootloops. If you cannot recover from a bad module without the normal Android UI, do not flash this. The companion APK's service/AIDL round-trip is instrumented, but the live root-module paths above are not proven on release hardware yet. Even correct builds may fail on strict SELinux policies, unusual vendor HALs, unsupported ABIs, or Magisk/LSPosed combinations this project has not tested.
- What is Echidna
- High-Risk Notice
- Features
- Quickstart
- Release Files
- Install Methods
- How Echidna Functions
- Architecture
- Documentation
- Comparison & Limitations
- Contributing
- License
Echidna is a native-first, root-based real-time voice changer for Android. Instead of
routing audio through a virtual device or a userspace app, it uses Zygisk + LSPosed to
inject processing into selected target processes. The current normal-flow native candidates are
AAudio, OpenSL ES, and tinyalsa; the LSPosed shim covers Java AudioRecord. Native
AudioRecord and raw libc capture are developer-contract routes, while Audio HAL and
AudioFlinger transforms are deliberately unsupported until a safe injection boundary exists.
An Android legacy input-preprocessor effect ABI is packaged and can be registered for the next boot
on proven legacy-HIDL system/vendor configurations. It remains default-off and is never
auto-applied. An experimental companion toggle only permits authorized LSPosed attachment to an
eligible AudioRecord session; enabling it is not proof that the effect loaded or processed audio.
Captured PCM is processed by a C++17 DSP engine (libech_dsp.so) and written back in place.
A Jetpack Compose companion app drives presets, per-app profiles, diagnostics, and safety
controls. It targets power users and researchers on rooted devices; it is not a Play Store app.
Intended module failsafes are documented for users and release testers:
- Create Magisk's module disable marker for
echidnabefore rebooting if the module looks wrong. - Create
/data/adb/echidna/disableor boot via the project's safe-mode path to keep Echidna inactive. - Create
/cache/echidna-disableor/metadata/echidna-disablefrom recovery when/datais not writable or not mounted yet. - Treat the automatic boot watchdog as a last-resort guard, not as a substitute for knowing how to disable the module yourself.
Already bootlooping? Follow the step-by-step recovery ladder in
Recovering from a bootloop — boot with all modules off, create an Echidna disable
marker, or, as a last resort, fastboot flash boot boot.img to remove Magisk (method contributed by
issue #17).
| Area | Capabilities |
|---|---|
| Capture routes | AAudio, OpenSL, and tinyalsa native candidates; LSPosed Java AudioRecord fallback; a non-shipping Phase 1 legacy preprocessor; explicit developer-only and unsupported routes. |
| DSP effects | Noise gate, EQ, compressor/AGC, pitch shift, formant shift, Auto-Tune, reverb, dry/wet mix — SIMD-tuned. |
| Presets | Built-in catalog (Natural Mask, Darth Vader, Helium, Radio Comms, Studio Warm, Robotizer, Cher-Tune, Anonymous) with import/export. |
| Per-app whitelist | Enable Echidna per application and bind a specific preset to each app. |
| Latency modes | Low-latency in-callback processing and a hybrid worker pipeline. |
| Quick Settings tile | Toggle the engine from the notification shade. |
| Diagnostics | Latency and CPU sampling, symbol-scan logs, tuner, compatibility wizard. |
| Safety | Panic bypass, auto-bypass on overload, SELinux-aware deployment, fail-closed defaults. |
Full, host-verified steps live in docs/build-install.md. In short:
git clone https://github.com/supermarsx/echidna.git
cd echidna
# 1. Build the per-ABI native libraries (needs the Android NDK)
ANDROID_NDK=/path/to/ndk bash tools/build_native_ndk.sh
# …or reproducibly in Docker:
docker build -t echidna-native docker/native-build && \
docker run --rm -v "$PWD:/workspace" echidna-native
# 2. Package the flashable Magisk module
bash tools/build_magisk_module.sh # -> out/echidna-magisk.zip
# 3. Build the companion APK and, if needed, the LSPosed shim
cd android/app && ./gradlew :app:assembleDebug # or assembleRelease with a keystore
cd ../lsposed-shim && ./gradlew :shim:assembleReleaseThen, on a rooted device (Magisk 24+ with Zygisk enabled, LSPosed installed):
- Flash
out/echidna-magisk.zipin Magisk and reboot. - Install the companion APK and open it.
- Enable the Echidna module in Zygisk / LSPosed.
- Add your target apps in the per-app whitelist and pick a preset.
- Run the compatibility wizard and diagnostics before enabling hooks for production apps.
Policy delivery supports multiple authenticated targets: Zygisk readers use UID-scoped policy v2 over the service-owned abstract socket, while LSPosed uses a caller/process-scoped read-only Binder provider. Do not assign the same process to both capture owners. See Limitations.
Current validation goes beyond a compile: Android instrumentation proves the in-app control service
and native processBlock path on Android 13/14 emulators. A rooted-emulator
AudioRecord.read probe also passed before the current explicit-contract route redesign; it is
historical evidence, not proof that the current native AudioRecord route is reachable. Magisk
flashing, live LSPosed injection, and every current capture route still need device validation.
Every successful main CI release publishes a GitHub Release with the same asset set. A normal
install needs exactly two of them: the companion APK and the Magisk zip. The three .zip bundles
are repackagings of files you already have, kept for offline, manual, and integrator use.
Per-package detail — contents, install path, and when to prefer one over another — is in
Release Packages.
Installable packages
echidna-companion-<tag>.apk- install this first. The Compose UI and in-process control service (libechidna_control_jni.so), plus per-ABIlibech_dsp.soso the Lab can process your own audio with no root, the wholedocs/tree bundled as offline in-app help, and — when the build has one — a bundled copy of the module zip that makes the guided installer turnkey. It is the only package that is useful on its own.echidna-magisk-<tag>.zip- the flashable Magisk/Zygisk module:zygisk/<abi>.soengine,libs/<abi>/libech_dsp.so, the default-offpreproc/<abi>/libechidna_preproc.soregistration source, the installer stub, boot/trust scripts,sepolicy.rule, and thecommon/release-cert-sha256pin that binds the module to the companion's certificate. Needed by anyone who wants audio transformed in other apps; a reboot is always required.echidna-lsposed-shim-<tag>.apk- optional JavaAudioRecordfallback for LSPosed-scoped apps, bundling its own per-ABIlibechidna_shim_jni.soandlibech_dsp.so. Install it only when you need or are testing that path.
Convenience packages — not needed for a normal install
echidna-apks-<tag>.zip- the companion and shim APKs underapks/plus a short README. CI verifies both members are byte-identical to the standalone APKs.echidna-native-libs-<tag>.zip- rawnative-libs/<abi>/outputs (engine, DSP, shim JNI bridge, legacy preprocessor) for inspection, diffing, or integrators. Not an install path: no installer, no boot scripts, no certificate pin.echidna-complete-<tag>.zip- the five assets above plusRELEASE_ARTIFACTS.md, for archiving a whole release or mirroring it offline.SHA256SUMS.txtis generated afterwards, so it is not inside this archive.SHA256SUMS.txt/RELEASE_ARTIFACTS.md- hashes for every other asset, and the short manifest that Release Packages expands on. Verify withsha256sum --ignore-missing -c SHA256SUMS.txt.
Use the newest release unless you are intentionally rolling back or already understand the recovery risk. Earlier releases can contain boot/module bugs that were fixed later and may be harder to recover from after flashing.
The companion's guided installer can also fetch a release for you: it resolves releases/latest at
run time, shows the tag and asset name before fetching a byte, and accepts the module zip only after
its SHA-256 matches the release's SHA256SUMS.txt and its common/release-cert-sha256 pin
matches the certificate that signed the running app. The convenience bundles are refused by that
path by design — a plain zip carries neither an APK signature nor a certificate pin, so nothing
binds it to your installed app. Download those by hand and check them against SHA256SUMS.txt.
Hosted releases are signed with a configured release certificate and fail closed if signing inputs are absent or invalid. An older debug-signed companion or shim cannot be upgraded in place to an APK signed by a different certificate; back up needed app data, uninstall the old package once, and then install the release-signed APK. See Signing.
For a normal install, use the companion APK plus the Magisk zip. Add the LSPosed shim only when you need or are testing the Java fallback path. Do not scope the same target app into both Zygisk and LSPosed unless you are intentionally testing duplicate-hook behavior.
- Prebuilt release: download the release assets, verify
SHA256SUMS.txt, install the companion APK, flash the Magisk zip, reboot, then run Diagnostics. - Local host build: build APKs with Gradle, cross-compile native libraries
with
tools/build_native_ndk.sh, then packageout/echidna-magisk.zip. - Docker build: use
docker/compose.yamlto run native build, Android build, and Magisk packaging with pinned toolchains. - Development/debug APK only: install
app-debug.apkwhen you only want to inspect UI behavior. No audio is transformed until the Magisk module/native path is installed.
Device activation is always manual: enable Zygisk in Magisk, install or enable LSPosed only if you need the fallback shim, whitelist target apps in the companion, then run the compatibility wizard.
Echidna has several operating paths:
- Native-first Zygisk path: the Magisk module loads
libechidna.sointo selected target processes. AAudio, OpenSL, and tinyalsa are the normal-flow capture candidates and route PCM throughlibech_dsp.so; all remain device/vendor gated. - LSPosed Java fallback: the optional shim covers Java
AudioRecordapps that never hit the native hook path through a dedicated JNI bridge and DSP library. It fetches strict v2 policy from an explicit read-only Binder provider that authenticates the target UID and claimed process. - Legacy input preprocessor: the standard legacy effect ABI boundary and its DSP context pass host ABI/lifecycle/audio/real-time tests. Eligible devices can stage next-boot registration, and the default-off experimental setting permits LSPosed to request authorized per-session attachment. Device load, enforced-SELinux activation, and audio-mutation proof remain outstanding.
- Developer-contract routes: native
AudioRecordand raw libc/dev/sndcapture stay disabled unless an explicit sample-rate/channel/format contract is supplied by a developer. - Unsupported boundaries: Audio HAL and AudioFlinger report
unsupported_injection_boundary; current app-process Zygisk injection cannot safely own them. - Compatibility mode: the app can prefer safer fallback behavior on fragile devices or unknown HALs while still keeping controls available.
- Bypass and panic: bypass passes audio through untouched. Panic is an independent timed admission gate that preserves the configured master/bypass state and publishes a new generation when it expires.
Echidna is four cooperating layers. The companion app hosts an in-process private control
service plus an authenticated read-only policy provider. Strict, monotonic policy v2 reaches
Zygisk through a UID-scoped service-owned abstract socket and LSPosed through process-scoped Binder.
The Zygisk native module injects into selected target processes, discovers eligible audio
symbols, and streams captured buffers to the DSP engine. An LSPosed Java shim covers Java
AudioRecord paths. Read the full walkthrough — hook order, IPC, and data flow — in
docs/architecture.md and the reasoning
behind each choice in docs/design-rationale.md.
Capture source
├─ operational candidates: AAudio / OpenSL / tinyalsa
├─ LSPosed fallback: Java AudioRecord
├─ device-gated: legacy input preprocessor (default-off authorized attachment candidate)
├─ developer contract: native AudioRecord / libc read
└─ unsupported: Audio HAL / AudioFlinger
↓ eligible buffers
DSP (libech_dsp.so)
Stack: Kotlin + Jetpack Compose (Material3) companion app · in-app control service (AIDL, JNI) · Java/Xposed LSPosed shim · C++17 native (CMake + NDK r27, per-ABI) · flashable Magisk/Zygisk module.
The full documentation site is built with MkDocs Material and published to GitHub Pages.
| Topic | Page |
|---|---|
| Architecture & data flow | architecture.md |
| Design rationale | design-rationale.md |
| Why this is hard to build | why-hard.md |
| Release packages | release-packages.md |
| Build & install | build-install.md |
| Recovering from a bootloop | recovery.md |
| DSP & effects reference | dsp-effects.md |
| Screenshots gallery | screenshots.md |
| Comparison vs alternatives | comparison.md |
| Limitations | limitations.md |
| E2E verification report | verification.md |
| Performance methodology | performance-testing.md |
| Vendor HAL analysis | vendor-hal-analysis.md |
| Developer guide · Signing · Magisk release | developer_readme.md · signing.md · magisk_release.md |
Unlike app-level tools (RootlessJamesDSP, Voicemod-style desktop apps, Clownfish) or generic
Magisk audio-mod modules, Echidna intercepts inside the target process for true real-time,
per-app transformation. That power comes with constraints: it requires root, behaviour varies
across OEM audio HALs and SELinux policies, armeabi-v7a hooking degrades gracefully (fails
closed), and HAL/AudioFlinger transformation is not implemented. Policy delivery is authenticated
and process-scoped, but capture-owner configuration still must not assign both hook stacks. See
comparison.md and limitations.md for the honest
detail.
Contributions that respect the native-first architecture and DSP performance goals are welcome:
- Follow the coding standards in
agents.mdfor C++17, Kotlin/Java, and scripting. - Keep commits focused, with descriptive messages and documented test results.
- Update the relevant docs when you change user-visible behaviour or preset formats.
- Run the native, Android, and DSP tests before opening a pull request (or document why they could not run). See build-install.md and verification.md.
Echidna is released under an MIT-derived non-commercial license with a separate commercial license requirement — see license.md. Use it responsibly: only transform audio you are authorised to modify, and comply with the terms of the apps and services you use it with.