Android application prototype for real-time Bluetooth microphone capture and playback using Kotlin + C++ (JNI) with Oboe for low-latency audio streaming.
This project demonstrates an Active Noise Cancellation pipeline scaffold for True Wireless Stereo (TWS) devices:
- Android UI to start/stop processing
- Runtime permission handling for audio and Bluetooth
- Bluetooth SCO routing for voice communication audio path
- Native audio engine using Oboe input/output streams
- DSP insertion point in native callback (currently placeholder attenuation)
Current DSP behavior is a placeholder (
output = input * 0.9) and not a full ANC/noise suppression algorithm yet.
- Android: Kotlin, AppCompat, ConstraintLayout
- Native: C++17, JNI
- Audio: Google Oboe (
com.google.oboe:oboe:1.8.0) - Build: Gradle Kotlin DSL, AGP 8.1.4, Kotlin 1.9.0
- CI: GitHub Actions (assembleDebug on pushes/PRs to
main)
ANC-application/
├── app/
│ ├── build.gradle.kts
│ └── src/main/
│ ├── AndroidManifest.xml
│ ├── java/com/example/twsnoisecanceller/MainActivity.kt
│ ├── cpp/
│ │ ├── CMakeLists.txt
│ │ └── native-lib.cpp
│ └── res/
│ ├── layout/activity_main.xml
│ ├── values/{strings,themes}.xml
│ ├── xml/{backup_rules,data_extraction_rules}.xml
│ └── mipmap*/drawable* launcher assets
├── .github/workflows/android-build.yml
├── build.gradle.kts
├── settings.gradle.kts
└── gradle.properties
- User taps toggle in
MainActivity. - App checks runtime permissions and requests missing ones.
- On start:
- Sets
AudioManager.MODE_IN_COMMUNICATION - Starts Bluetooth SCO and enables SCO route
- Calls native
startDenoise()
- Sets
- Native engine:
- Opens low-latency output stream with callback
- Opens matching input stream (
VoiceCommunicationpreset) - In output callback, reads mic frames from input stream
- Runs DSP placeholder and writes to output buffer
- On stop/destroy:
- Stops native streams
- Disables SCO and restores
MODE_NORMAL
android.hardware.microphone(required)android.hardware.bluetooth(required)
RECORD_AUDIOMODIFY_AUDIO_SETTINGS- Android 12+ (
SDK 31+):BLUETOOTH_CONNECT - Android 11 and below:
BLUETOOTH,BLUETOOTH_ADMIN
- minSdk: 26
- targetSdk / compileSdk: 34
- Android device with Bluetooth + microphone
- Preferably a TWS/Bluetooth headset supporting SCO voice path
- JDK 17
- Android SDK / Android Studio with NDK + CMake support
- Gradle (wrapper or local install)
gradle assembleDebug --no-daemonAPK output:
app/build/outputs/apk/debug/app-debug.apk
- Open repository in Android Studio.
- Let Gradle sync.
- Connect device/emulator (Bluetooth tests require real device).
- Run
appconfiguration. - Grant requested permissions, then toggle ANC on/off.
Workflow: .github/workflows/android-build.yml
- Triggers on push and pull request to
main - Sets up JDK 17 + Gradle 8.4
- Runs
gradle assembleDebug --no-daemon - Uploads
app-debug.apkartifact
The key insertion point for ANC/noise suppression is in:
app/src/main/cpp/native-lib.cppinsideonAudioReady(...)
Replace placeholder frame processing with:
- RNNoise integration
- Echo cancellation / adaptive filtering
- Additional gain control / filtering chain as needed
- No production ANC algorithm yet (placeholder pass-through attenuation)
- Uses
Thread.sleep(1000)on start while waiting for SCO (blocking UI) - No explicit SCO state broadcast handling
- No advanced error recovery for stream interruptions/disconnects
- No automated tests currently included
- Replace placeholder DSP with real frame-based denoiser/ANC
- Move SCO startup flow to async state-driven handling
- Add robust Oboe error callbacks and stream restart strategy
- Add instrumentation tests for permission/routing lifecycle
- Improve UX/state feedback for Bluetooth routing and failures
No license file is currently included in this repository.