# How Meetily's Professional Audio Mixer Applies RMS-Based Ducking: A Technical Deep Dive

> Discover how Meetily's audio mixer uses RMS-based ducking to automatically reduce system audio gain during speech, preventing clipping and enhancing clarity. Learn the technical details.

- Repository: [Zackriya Solutions/meetily](https://github.com/Zackriya-Solutions/meetily)
- Tags: deep-dive
- Published: 2026-07-20

---

**Meetily synchronizes microphone and system audio streams in a lock‑free ring buffer, calculates the Root‑Mean‑Square (RMS) energy of each 50 ms microphone window to detect speech activity, and automatically attenuates system audio gain to 0.60 (a 40 % reduction) when speech is present while preventing digital clipping through sample clamping and soft scaling.**

Meetily, an open‑source AI meeting assistant by Zackriya‑Solutions, records both microphone input and system audio simultaneously to create complete meeting archives. To ensure voice remains intelligible over background music or notifications, the Rust codebase implements professional RMS‑based ducking that dynamically adjusts stream levels in real time. This article examines the precise algorithms and data structures found in [`pipeline.rs`](https://github.com/Zackriya-Solutions/meetily/blob/main/pipeline.rs) and [`ffmpeg_mixer.rs`](https://github.com/Zackriya-Solutions/meetily/blob/main/ffmpeg_mixer.rs) that make this broadcast‑grade mixing possible.

## Audio Mixing Architecture Overview

The audio pipeline centers on two complementary implementations that both enforce the same RMS‑based ducking semantics.

**`AudioMixerRingBuffer`** and **`ProfessionalAudioMixer`** live in [`frontend/src-tauri/src/audio/pipeline.rs`](https://github.com/Zackriya-Solutions/meetily/blob/main/frontend/src-tauri/src/audio/pipeline.rs) and provide a lightweight, single‑file solution. They use a fixed‑size `VecDeque` to align incoming mic and system samples into synchronized 50 ms windows before mixing.

**`FFmpegAudioMixer`** in [`frontend/src-tauri/src/audio/ffmpeg_mixer.rs`](https://github.com/Zackriya-Solutions/meetily/blob/main/frontend/src-tauri/src/audio/ffmpeg_mixer.rs) offers a full‑featured alternative with separate per‑source buffers, configurable parameters, and additional gap handling for Bluetooth devices. Both implementations share identical ducking logic: when microphone RMS exceeds the speech threshold, system gain is reduced; otherwise, it remains at full volume.

## The RMS-Based Ducking Algorithm Step-by-Step

### Window Extraction and Synchronization

Before any level detection occurs, the `AudioMixerRingBuffer` ensures temporal alignment between the two asynchronous capture streams.

When both buffers contain at least `window_size_samples` (equivalent to 50 ms at the current sample rate), the `extract_window` method returns aligned slices `(mic_window, system_window)`. The ring buffer pads missing data with silence to avoid artifacts and drops samples older than a 400 ms safety limit to prevent unbounded memory growth.

```rust
// Conceptual flow from pipeline.rs
let (mic_window, sys_window) = ring_buffer.extract_window();

```

### RMS Calculation and Speech Detection

With aligned windows available, the mixer calculates the microphone's energy using the standard RMS formula.

The `calculate_rms` function (or its inline equivalent in `ProfessionalAudioMixer`) computes the square root of the mean squared amplitude:

```rust
fn calculate_rms(samples: &[f32]) -> f32 {
    let sum_squares: f32 = samples.iter().map(|s| s * s).sum();
    (sum_squares / samples.len() as f32).sqrt()
}

```

The resulting value is compared against `SPEECH_THRESHOLD = 0.01`. An RMS above this constant indicates active speech, triggering the ducking behavior.

### Dynamic Gain Reduction

When speech is detected, the mixer applies a hard‑coded ducking factor to the system audio stream.

- **Speech present** (`rms > 0.01`): `system_gain = self.system_ducking` (default **0.60**, approximately ‑4.4 dB)
- **Silence**: `system_gain = 1.0` (0 dB, full volume)

The microphone stream may also be scaled by `mic_ducking` (typically 1.0) to balance the mix. This gain structure ensures the microphone remains the focal point without completely muting system sounds.

### Mixed Output and Clipping Prevention

The final mix combines the scaled samples and enforces hard limits to prevent digital distortion.

For each sample pair, the algorithm computes:

```rust
let mixed = (mic_sample * self.mic_ducking) + (system_sample * system_gain);
result.push(mixed.clamp(-1.0, 1.0));

```

The `clamp` operation prevents values outside the normalized ‑1.0 to +1.0 range. In the `ProfessionalAudioMixer` implementation, an additional soft‑scaling step proportionally reduces the entire window if the sum exceeds unity, preserving the dynamic relationships between samples while eliminating harsh clipping.

## Implementation in pipeline.rs

The primary mixing logic resides in [`frontend/src-tauri/src/audio/pipeline.rs`](https://github.com/Zackriya-Solutions/meetily/blob/main/frontend/src-tauri/src/audio/pipeline.rs), which defines both the synchronization primitive and the ducking mixer.

The `AudioPipeline` struct orchestrates the flow: it receives raw `AudioChunk`s from the capture layer (microphone and system), pushes them into the `AudioMixerRingBuffer`, extracts synchronized windows, and delegates mixing to `ProfessionalAudioMixer::mix_window`. The resulting audio is then forwarded to both the VAD → Whisper transcription pipeline and the WAV recorder via `recording_sender_for_mixed`.

```rust
// From pipeline.rs - the core mixing method
fn mix(&mut self, mic: &[f32], system: &[f32]) -> Vec<f32> {
    let mic_rms = calculate_rms(mic);
    let is_speech = mic_rms > 0.01;               // speech threshold
    let system_gain = if is_speech { self.system_ducking } else { 1.0 };

    mic.iter()
        .zip(system.iter())
        .map(|(&m, &s)| (m * self.mic_ducking + s * system_gain).clamp(-1.0, 1.0))
        .collect()
}

```

## Advanced Ducking in ffmpeg_mixer.rs

For scenarios requiring more robust buffer management (such as handling Bluetooth audio gaps), [`frontend/src-tauri/src/audio/ffmpeg_mixer.rs`](https://github.com/Zackriya-Solutions/meetily/blob/main/frontend/src-tauri/src/audio/ffmpeg_mixer.rs) provides `FFmpegAudioMixer`.

This implementation maintains independent resampling buffers for each input source and applies the identical RMS detection and ducking logic, but adds configurable latency compensation and drift correction. The ducking factor remains `0.60` during speech activity, demonstrating that the RMS‑based approach is consistent across both the simple and advanced mixer variants.

## Integration Example

To utilize this system, the Tauri backend initializes an `AudioPipelineManager` which wires together the capture devices, ring buffer, mixer, and recording sink:

```rust
let mut manager = AudioPipelineManager::default();
manager.start(
    state.clone(),
    transcription_sender,
    /* target_chunk_duration_ms */ 0,
    /* sample_rate */ 48000,
    Some(recording_sender),          // receive mixed audio for WAV files
    mic_device_name,
    mic_device_kind,
    system_device_name,
    system_device_kind,
)?;

```

The manager spawns an async `AudioPipeline::run` loop that continues processing until the sender channel closes, guaranteeing all buffered audio flushes to disk before shutdown.

## Summary

- **Meetily** uses a lock‑free `AudioMixerRingBuffer` in [`pipeline.rs`](https://github.com/Zackriya-Solutions/meetily/blob/main/pipeline.rs) to synchronize microphone and system audio into 50 ms aligned windows.
- **RMS detection** calculates the energy of each microphone window; values above `0.01` trigger ducking.
- **Ducking attenuation** reduces system audio gain to **0.60** (40 % reduction) during speech, restoring full volume during silence.
- **Clip protection** uses `clamp(-1.0, 1.0)` and soft‑scaling to keep mixed output within valid floating‑point ranges.
- **Two implementations** exist: the lightweight `ProfessionalAudioMixer` for standard use, and `FFmpegAudioMixer` for advanced device handling with identical ducking semantics.

## Frequently Asked Questions

### What is RMS-based ducking in audio processing?

RMS‑based ducking is a dynamic range compression technique that measures the Root‑Mean‑Square energy of a control signal (like a microphone) to automatically reduce the volume of another audio stream (like system audio) when speech is detected. Meetily implements this in Rust by calculating the RMS of 50 ms microphone windows and attenuating system gain by 40 % whenever the energy exceeds the 0.01 threshold.

### Why does Meetily use a 50ms window size for audio mixing?

The 50 ms window size strikes a balance between responsive ducking and stable level detection. Windows shorter than 20 ms capture transient noise that could trigger false ducking, while windows longer than 100 ms introduce noticeable latency in the ducking response. The 50 ms duration aligns with the ring buffer implementation in [`pipeline.rs`](https://github.com/Zackriya-Solutions/meetily/blob/main/pipeline.rs) and matches typical speech syllable durations for natural‑sounding gain changes.

### How does Meetily prevent audio clipping when mixing multiple streams?

The mixer prevents clipping through a two‑stage protection mechanism: first, it applies `clamp(-1.0, 1.0)` to every summed sample to enforce hard limits; second, the `ProfessionalAudioMixer` performs a soft‑scaling pass that proportionally reduces the entire window if the peak exceeds unity gain. This combination eliminates digital distortion while preserving the relative dynamics between microphone and system audio.

### What is the difference between ProfessionalAudioMixer and FFmpegAudioMixer?

`ProfessionalAudioMixer` (in [`pipeline.rs`](https://github.com/Zackriya-Solutions/meetily/blob/main/pipeline.rs)) is a streamlined implementation using a shared ring buffer for straightforward use cases. `FFmpegAudioMixer` (in [`ffmpeg_mixer.rs`](https://github.com/Zackriya-Solutions/meetily/blob/main/ffmpeg_mixer.rs)) provides a more robust architecture with separate per‑source buffers, configurable parameters, and specialized handling for Bluetooth device gaps and clock drift. Both apply identical RMS‑based ducking logic with the same 0.60 gain reduction factor during speech activity.