flutter_nnnoiseless 1.4.0
flutter_nnnoiseless: ^1.4.0 copied to clipboard
Recurrent neural network for audio noise reduction .
Flutter NNNoiseless #
Real-time and batch audio noise reduction for Flutter. A port of the nnnoiseless Rust crate (RNNoise), powered by Flutter Rust Bridge.
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:
-
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-corpFor local development:
flutter run -d chrome \ --web-header=Cross-Origin-Opener-Policy=same-origin \ --web-header=Cross-Origin-Embedder-Policy=require-corpHosts that cannot set response headers (e.g. GitHub Pages) can use the coi-serviceworker workaround.
-
File-based APIs are not available:
denoiseFileandpcmToWavthrowUnsupportedErroron web since browsers have no file paths. Use sessions to denoise audio buffers, andpackage:wav'sWav.writeif 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.