denoise
Real-time noise cancellation for phone calls — RNNoise + Wiener filter pipeline in C, exposed to Flutter via FFI
How it works
Mic input → RNNoise (neural) → Wiener filter (classical) → Noise gate → Clean output
Results
| Stage | RMS | Noise Reduction |
|---|---|---|
| Original | 1449.09 | - |
| After RNNoise | 1289.24 | 11.0% |
| After Wiener | 686.81 | 52.6% |
Audio Proof: Listen to the audio quality below. GitHub will render these as playable audio widgets:
Original Noisy Audio: https://github.com/LikhinMN/denoise/blob/main/test.wav
Cleaned Output Audio: https://github.com/LikhinMN/denoise/blob/main/output_clean.wav
Architecture
The core audio processing pipeline is written entirely in C for maximum performance and determinism. It combines two powerful approaches:
- RNNoise: An efficient RNN-based deep learning model trained on human speech, providing robust primary broadband noise suppression.
- Wiener Filter: A classical frequency-domain filter with VAD (Voice Activity Detection) gating, tuned to surgically suppress any remaining noise during silence frames while preserving voice harmonics when speech is present.
This C pipeline is cross-compiled into a self-contained shared library (libnoisecanceller.so). Using Dart FFI, the Flutter frontend streams raw 16-bit PCM bytes from the microphone directly into the native processing engine in real-time, retrieving cleaned PCM chunks and serving them directly to the device speaker using a custom StreamAudioSource.
Tech Stack
- C (Core DSP pipeline)
- RNNoise (Recurrent neural network noise suppression)
- KissFFT (Lightweight Fast Fourier Transforms)
- Dart FFI (C-to-Dart interoperability)
- Flutter (Mobile app frontend)
Build Instructions
1. Compile the Shared Library
Using the Android NDK, cross-compile the C pipeline into an arm64-v8a shared library directly into the Flutter project's jniLibs folder:
cd denoise
<ANDROID_NDK_PATH>/toolchains/llvm/prebuilt/linux-x86_64/bin/aarch64-linux-android35-clang \
-shared -fPIC -Wall -Isrc -Irnnoise/include -Irnnoise/src \
-o denoise_app/android/app/src/main/jniLibs/arm64-v8a/libnoisecanceller.so \
src/noisecanceller.c src/wiener.c src/kiss_fft.c \
rnnoise/src/denoise.c rnnoise/src/rnn.c rnnoise/src/pitch.c \
rnnoise/src/kiss_fft.c rnnoise/src/celt_lpc.c rnnoise/src/nnet.c \
rnnoise/src/nnet_default.c rnnoise/src/parse_lpcnet_weights.c \
rnnoise/src/rnnoise_data.c rnnoise/src/rnnoise_tables.c -lm
2. Run the Flutter App
With the .so binary in place, compile and run the application:
cd denoise_app
flutter pub get
flutter run
Performance
- Latency: ~2.9ms per chunk
- Overhead: Extremely low footprint. The combination of lightweight C + FFI makes real-time on-device streaming seamless.