Back to projects
C

denoise

Real-time noise cancellation using RNNoise + Wiener filter in C, exposed to Flutter via FFI.

CDSPRNNoiseDart FFIFlutter

denoise

Real-time noise cancellation for phone calls — RNNoise + Wiener filter pipeline in C, exposed to Flutter via FFI

Build Platform Language

How it works

Mic input → RNNoise (neural) → Wiener filter (classical) → Noise gate → Clean output

Results

StageRMSNoise Reduction
Original1449.09-
After RNNoise1289.2411.0%
After Wiener686.8152.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:

  1. RNNoise: An efficient RNN-based deep learning model trained on human speech, providing robust primary broadband noise suppression.
  2. 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.