Skip to content

sk3llo/flutter_nnnoiseless

Repository files navigation

Flutter NNNoiseless

Real-time and batch audio noise reduction for Flutter. A port of the nnnoiseless Rust crate (RNNoise), powered by Flutter Rust Bridge.

pub Buy Me A Coffee

Features

  • Real-time denoising of raw PCM audio streams, such as microphone input
  • Voice activity detection (VAD): a speech probability for every 10ms frame
  • Batch denoising of audio files (WAV, FLAC, MP3, OGG, M4A/AAC), with progress reporting and cancellation
  • Multiple concurrent sessions, each with fully isolated state
  • Multi-channel audio: interleaved PCM with up to 8 channels per session
  • Custom RNNoise models trained on your own noise profiles
  • Built-in streaming resampling: feed any sample rate, get the same rate back
  • Adjustable suppression strength via a dry/wet mix parameter
  • Runs everywhere Flutter does, including the browser (bundled WebAssembly)
  • No Rust toolchain required: prebuilt, signed native libraries are downloaded automatically at build time

Supported platforms

Platform Supported Minimum version
Android SDK 23
iOS 11.0
macOS 10.15
Windows Windows 10
Linux Ubuntu 20.04+ (x64, arm64)
Web Browsers with SharedArrayBuffer (see Web)

Requires Flutter 3.0.0 or higher.

Installation

flutter pub add flutter_nnnoiseless

No further setup is needed in most cases. If a prebuilt binary is not available for your platform, the build falls back to compiling from source, which requires a Rust installation.

Usage

Real-time denoising

Create a NoiselessSession per audio stream. Each session owns its own denoiser state, so multiple sessions can run concurrently and nothing leaks between recordings.

The simplest way is to pipe a raw 16-bit PCM mono stream (for example from the record package) through the session's transformer:

final session = await NoiselessSession.create(sampleRate: 48000);

micStream.transform(session.transformer).listen(playOrSave);

Alternatively, process chunks yourself:

final result = await session.process(chunk);
save(result.audio); // denoised PCM at your sample rate

At the end of a recording:

final tail = await session.flush(); // drain buffered audio
await session.reset();              // reuse for the next recording...
session.dispose();                  // ...or release it

Sample rates other than 48000Hz are resampled internally and the denoised audio is returned at your input rate. The wet parameter of NoiselessSession.create (0.0 to 1.0) controls how aggressive the suppression is.

Sessions handle interleaved multi-channel audio and custom RNNoise models too:

final stereo = await NoiselessSession.create(channels: 2);

final custom = await NoiselessSession.create(
  model: await File('my_model.rnn').readAsBytes(),
);

Voice activity detection

process returns the RNNoise speech probability for every 10ms frame:

final result = await session.process(chunk);

print(result.voiceProbability);   // 0.0 to 1.0, max over the chunk
print(result.voiceProbabilities); // per-frame values

if (result.isVoice()) {
  showSpeakingIndicator();
}

Denoising a file

final token = NoiselessCancelToken();

await Noiseless.instance.denoiseFile(
  inputPathStr: 'assets/noisy_recording.mp3',
  outputPathStr: 'assets/output.wav',
  onProgress: (fraction) => print('${(fraction * 100).round()}%'),
  cancelToken: token, // token.cancel() aborts with DenoiseCancelledException
  wet: 0.9,           // optional: suppression strength
);

All parameters besides the paths are optional (onProgress, cancelToken, wet, and model for custom RNNoise weights). Input can be WAV (any bit depth), FLAC, MP3, OGG/Vorbis, or M4A/AAC at any sample rate. The output is written as 16-bit WAV at the input's sample rate and channel count.

Saving PCM output as WAV

await Noiseless.instance.pcmToWav(
  pcmData: denoisedBytes,
  outputPath: '/path/to/output', // '.wav' is appended automatically
  sampleRate: 48000,
);

Web

On web the Rust denoiser runs as WebAssembly, bundled with this package, so NoiselessSession works with no extra build steps. Two things to know:

  1. Your app must be served with cross-origin isolation headers, because the denoiser runs in a Web Worker backed by shared memory:

    Cross-Origin-Opener-Policy: same-origin
    Cross-Origin-Embedder-Policy: require-corp
    

    For local development:

    flutter run -d chrome \
      --web-header=Cross-Origin-Opener-Policy=same-origin \
      --web-header=Cross-Origin-Embedder-Policy=require-corp

    Hosts that cannot set response headers (e.g. GitHub Pages) can use the coi-serviceworker workaround.

  2. File-based APIs are not available: denoiseFile and pcmToWav throw UnsupportedError on web since browsers have no file paths. Use sessions to denoise audio buffers, and package:wav's Wav.write if you need WAV bytes for download.

How it works

The heavy lifting is done in Rust by nnnoiseless, a rewrite of the RNNoise recurrent neural network. Audio is processed in 480-sample frames at 48kHz; other sample rates are converted on the fly with a streaming resampler, so chunk boundaries stay artifact-free.

Native libraries for each platform are built in CI, signed, and attached to GitHub Releases. At build time cargokit downloads the binary for your target, verifies its signature, and links it into your app. If no prebuilt binary matches, it compiles the crate locally instead.

Example

The example app records from the microphone, denoises the stream in real time, logs the voice probability, and saves both the raw and denoised audio as WAV files for comparison.

License

Distributed under the LGPL-3.0 license. See LICENSE for details.

About

Recurrent neural network for audio noise reduction.

Topics

Resources

Stars

Watchers

Forks

Releases

Packages

Contributors

Languages