AI Noise Cancellation for Mobile Call Apps
The Problem: Background Noise in Mobile Calls
When you open the microphone via AVAudioSession on iOS or AudioRecord on Android, you get a raw PCM stream — with everything around the user. Construction work, coffee machines, children — all of it goes into the call. The standard Acoustic Echo Cancellation (AEC) from WebRTC removes echo but not background noise. We implement turnkey integration of ML noise suppression models — from architecture selection to publication in app stores. Our engineers have 5+ years of experience in audio processing and have delivered over 30 projects with voice interfaces.
How AI Noise Suppression Differs from Standard DSP
Classic approaches — spectral subtraction or Wiener filter: the noise model is estimated during speech pauses, then subtracted from the spectrum. Works for stationary noise (fan hum), fails on non-stationary noise (subway chatter, nearby keyboard).
AI approach — a neural network trained on pairs of clean speech + noisy speech predicts a mask for each spectrogram frame. Models like RNNoise or DTLN operate in real time, processing 10–20 ms frames with sub-frame latency.
How Real-Time AI Noise Cancellation Works
The ML model receives the signal's spectrogram, computes the probability of speech in each frequency bin, and applies a filter. This allows noise suppression even while the person is speaking — unlike gates. Voice Activity Detection (VAD) further reduces load by passing only speech segments.
Model Comparison: RNNoise vs DTLN
| Parameter | RNNoise | DTLN |
|---|---|---|
| Model Size | ~90 KB | ~2 MB |
| Latency | <1 ms | 8–35 ms (device-dependent) |
| Suppression Quality | Good for stationary noise | Excellent for complex, multi-component noise |
| Platform Support | C library (iOS/Android via NDK) | TFLite (Android) / Core ML (iOS) |
| Recommendation | Budget devices, resource constraints | Flagships, high quality requirements |
RNNoise: Quick Start on Both Platforms
RNNoise from Mozilla is a C library, 90 KB, ~2 MFLOPS per frame. It compiles into a static library for iOS (xcframework) and Android (AAR with native part via NDK).
// Initialization DenoiseState *st = rnnoise_create(NULL); // Process a frame (480 samples = 10 ms at 48 kHz) float frame[480]; // ... fill from microphone buffer float vad_prob = rnnoise_process_frame(st, frame, frame); // frame now contains clean signal // vad_prob > 0.5 — likely speech Integration on iOS: AVAudioEngine with a custom AVAudioSinkNode or a tap on the input node. Format — Float32, 48 kHz, mono. AVAudioSession must be configured with mode: .voiceChat and system processing explicitly disabled, otherwise iOS applies its own noise reduction on top of yours.
let inputNode = audioEngine.inputNode let format = inputNode.outputFormat(forBus: 0) inputNode.installTap(onBus: 0, bufferSize: 480, format: format) { [weak self] buffer, _ in guard let self = self else { return } let channelData = buffer.floatChannelData![0] // Pass to rnnoise_process_frame via C-bridge self.rnnoiseProcessor.process(channelData, frameLength: Int(buffer.frameLength)) } On Android — AudioRecord with AudioFormat.ENCODING_PCM_FLOAT, buffer size of 480 samples, processing in a separate thread with Process.THREAD_PRIORITY_URGENT_AUDIO. Call the same C library via JNI.
DTLN and Heavier Models
If RNNoise is insufficient (complex multi-component noise, multiple sources), we use DTLN — a two-stage LSTM model. It is converted to TFLite (Android) or Core ML (iOS).
In practice: DTLN at 16 kHz takes 8–14 ms per frame on iPhone 12, well within real-time. On Android Snapdragon 778G — similar. On budget Helio G85 — 25–35 ms, which creates cumulative latency.
For mobile use, choosing the sample rate is important: 16 kHz instead of 48 kHz reduces computational load by a factor of four, and for speech, bandwidth up to 8 kHz is sufficient for intelligibility.
Integration into WebRTC
WebRTC SDKs (LiveKit, Agora, Daily) provide AudioProcessingModule or a hook before encoding. In native WebRTC for iOS — custom RTCAudioProcessingModule:
// Register custom processing let config = RTCConfiguration() // Create RTCPeerConnectionFactory with custom AudioDeviceModule // or use AudioProcessingConfig for WebRTC built-in replacement Important nuance: WebRTC already includes AECM and NS (Noise Suppression). When enabling your own AI noise suppression, you need to disable the built-in NS via AudioProcessingConfig, otherwise double processing creates artifacts — "metallic" sound, clipped consonants.
What's Included in the Work (Deliverables)
- Audit of the current audio pipeline in the app
- Model selection (RNNoise/DTLN/custom) based on latency and quality requirements
- Compilation of native libraries for target architectures (arm64, x86_64 for simulator)
- Integration into existing audio stack (AVAudioEngine, AudioRecord, WebRTC)
- VAD tuning and elimination of double processing (disabling built-in NS)
- Testing with real-world noises (subway, street, office)
- Documentation and support for app store publication
Common Pitfalls in Implementation
- Forgetting to disable built-in noise suppression in WebRTC — results in "metallic" sound.
- Using 48 kHz for DTLN — latency becomes unacceptable.
- Not configuring VAD — model wastes resources on silence.
- Not checking compatibility across Android versions (AudioRecord has bugs on some firmware).
Estimated Timelines
Integration of RNNoise into an existing WebRTC stack: 1–2 weeks. Full implementation with DTLN/TFLite, VAD tuning, and dual-platform support: 3–5 weeks. Cost is calculated individually. Contact us for an assessment of your project — we will find the optimal solution and provide a consultation.







