# How to Implement RMS-Based Audio Ducking in Meetily to Prevent System Audio from Drowning Out Microphone

> Learn how to implement RMS-based audio ducking in Meetily. Prevent system audio from drowning out your microphone with this essential technique. Enhance your audio clarity now.

- Repository: [Zackriya Solutions/meetily](https://github.com/Zackriya-Solutions/meetily)
- Tags: how-to-guide
- Published: 2026-07-12

---

**TLDR: Meetily achieves RMS-based audio ducking by calculating the root-mean-square energy of microphone chunks in `AudioMixer::mix` and attenuating system audio to 60% volume (configurable via `system_ducking`) whenever the mic signal exceeds the `SPEECH_THRESHOLD` of 0.01, preventing background audio from overpowering speech.**

Meetily is an open-source meeting assistant that captures parallel audio streams from both microphone and system sources. When recording meetings with shared media or background applications, implementing RMS-based audio ducking in Meetily ensures the microphone remains intelligible by automatically reducing system volume during speech. The ducking logic lives entirely within the Rust-based audio pipeline in the `frontend/src-tauri/src/audio/` directory of the Zackriya-Solutions/meetily repository.

## Understanding the Audio Pipeline Architecture

Meetily's audio processing relies on four interconnected components that manage buffering, mixing, and device-specific timing.

- **SourceBuffer**: Located 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) (lines 78-110), this struct holds time-stamped audio chunks for each input source and applies adaptive timeouts based on whether the device is wired or Bluetooth.

- **AudioMixer**: The core ducking implementation 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) (lines 55-85). This struct performs RMS calculations and applies dynamic gain scaling when `adaptive_ducking` is enabled.

- **FFmpegAudioMixer**: A façade that orchestrates two `SourceBuffer` instances, calls `AudioMixer::mix`, and returns 50ms mixed windows. Defined 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) (lines 40-70).

- **AudioPipeline**: A higher-level manager in [`frontend/src-tauri/src/audio/pipeline.rs`](https://github.com/Zackriya-Solutions/meetily/blob/main/frontend/src-tauri/src/audio/pipeline.rs) (lines 45-90) that extracts windows and forwards them to the recorder. It falls back to `ProfessionalAudioMixer` when adaptive ducking is disabled.

## How RMS-Based Ducking Works

The ducking algorithm follows a five-step process that evaluates microphone energy and adjusts system gain in real-time:

1. **Capture**: `AudioCapture` feeds microphone and system audio into their respective `SourceBuffer` instances.

2. **Ready Check**: `FFmpegAudioMixer::has_data_ready` verifies both buffers contain sufficient data according to device-specific adaptive timeouts.

3. **RMS Calculation**: Inside `AudioMixer::mix`, the system computes the root-mean-square energy of the microphone chunk using `calculate_rms`. The code compares this against a calibrated threshold:

```rust
let mic_rms = calculate_rms(mic);
const SPEECH_THRESHOLD: f32 = 0.01;   // calibrated for meetings
let is_speech = mic_rms > SPEECH_THRESHOLD;

```

This logic appears 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) at lines 88-95.

4. **Dynamic Gain Application**: When speech is detected, the system audio gain is set to the `system_ducking` factor (default 0.60, representing 40% reduction). Otherwise, system gain remains at 1.0 (full volume).

5. **Mixing and Clipping**: Samples are mixed using the formula `mic * mic_ducking + system * system_gain` and clamped to `[-1.0, 1.0]` to prevent digital clipping (lines 103-108 in [`ffmpeg_mixer.rs`](https://github.com/Zackriya-Solutions/meetily/blob/main/ffmpeg_mixer.rs)).

## Implementing the Ducking Logic

### Basic Setup with FFmpegAudioMixer

To enable RMS-based ducking in your Meetily implementation, instantiate `FFmpegAudioMixer` with adaptive processing enabled. The following example configures the mixer for 48kHz audio with default ducking parameters:

```rust
use meetily::audio::ffmpeg_mixer::FFmpegAudioMixer;
use meetily::audio::device_detection::InputDeviceKind;

// Create the adaptive mixer (adaptive ducking enabled by default)
let mut mixer = FFmpegAudioMixer::new(
    "Built‑in Mic".into(),
    InputDeviceKind::Wired,
    "BlackHole 2ch".into(),
    InputDeviceKind::Wired,
    48_000,
);

// Feed raw chunks as they arrive from capture callbacks
mixer.push_mic(mic_chunk);
mixer.push_system(sys_chunk);

// Retrieve mixed 50ms windows when both buffers have data
if let Some(mixed) = mixer.pop_mixed() {
    // mixed is a Vec<f32> containing interleaved stereo samples
    // Forward to Whisper transcription or file recorder
    transcribe_audio(mixed);
}

```

The constructor is defined at `FFmpegAudioMixer::new` (lines 40-70) and the mixing retrieval at `FFmpegAudioMixer::pop_mixed` (lines 94-106).

### Customizing Ducking Parameters

For fine-grained control over the ducking behavior, construct a custom `AudioMixer` struct with modified gain values. This example reduces system audio to 40% during speech instead of the default 60%:

```rust
use meetily::audio::ffmpeg_mixer::{AudioMixer, FFmpegAudioMixer};
use meetily::audio::device_detection::InputDeviceKind;

// Configure custom AudioMixer with aggressive ducking
let custom_mixer = AudioMixer {
    mic_ducking: 1.0,           // maintain full microphone volume
    system_ducking: 0.4,        // reduce system to 40% during speech
    adaptive_ducking: true,     // enable RMS-based speech detection
};

// Construct FFmpegAudioMixer with custom configuration
let mut mixer = FFmpegAudioMixer {
    mic_buffer: SourceBuffer::new("Mic".into(), InputDeviceKind::Wired, 48_000),
    system_buffer: SourceBuffer::new("System".into(), InputDeviceKind::Wired, 48_000),
    mixer: custom_mixer,
    sample_rate: 48_000,
    mixing_window_samples: 2400, // 50ms at 48kHz
    windows_mixed: 0,
};

// Process audio as normal - custom system_ducking applies automatically
mixer.push_mic(microphone_data);
mixer.push_system(system_data);

```

The `AudioMixer` struct fields are defined at lines 57-63 in [`ffmpeg_mixer.rs`](https://github.com/Zackriya-Solutions/meetily/blob/main/ffmpeg_mixer.rs), with the default constructor implementation at lines 68-74.

## Configuring Speech Detection Thresholds

The sensitivity of the ducking trigger depends on two configurable constants in the `AudioMixer` implementation:

- **SPEECH_THRESHOLD**: Set to `0.01f32` by default (lines 88-95). Lower values trigger ducking more aggressively for quiet speech; higher values reduce false positives from background noise.

- **system_ducking**: Controls the attenuation depth. Values range from 0.0 (complete mute) to 1.0 (no ducking). The default 0.60 provides a balance where system audio remains audible but subordinate to microphone input.

To adjust the threshold for different acoustic environments, modify the constant 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) before compilation, or extend the `AudioMixer` struct to accept runtime threshold configuration.

## Summary

Meetily's RMS-based audio ducking provides automatic gain control that preserves microphone clarity during system audio playback:

- The `AudioMixer` 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) calculates microphone RMS energy and attenuates system audio by 40% when `SPEECH_THRESHOLD` is exceeded.
- **FFmpegAudioMixer** orchestrates the dual-buffer architecture, delivering 50ms mixed windows at 48kHz sample rate.
- Ducking behavior is controlled via the `system_ducking` parameter (0.0-1.0) and the `adaptive_ducking` boolean flag.
- The algorithm prevents clipping by clamping mixed output to `[-1.0, 1.0]` using the formula `mic * mic_ducking + system * system_gain`.

## Frequently Asked Questions

### How does Meetily detect when to apply audio ducking?

Meetily detects speech by calculating the root-mean-square (RMS) energy of each microphone audio chunk in `AudioMixer::mix`. When the calculated `mic_rms` value exceeds the `SPEECH_THRESHOLD` constant of 0.01, the system identifies active speech and triggers the ducking mechanism to reduce system audio volume according to the `system_ducking` parameter.

### Can I disable RMS-based ducking and use simple mixing instead?

Yes. Set the `adaptive_ducking` parameter to `false` when constructing the `AudioMixer`, or use the `ProfessionalAudioMixer` fallback located in [`frontend/src-tauri/src/audio/pipeline.rs`](https://github.com/Zackriya-Solutions/meetily/blob/main/frontend/src-tauri/src/audio/pipeline.rs). When disabled, the mixer sums both audio sources at full volume without RMS analysis or dynamic gain adjustments.

### What is the latency impact of the RMS calculation?

The RMS calculation adds negligible latency because it operates on 50ms windows (2400 samples at 48kHz) using a single pass through the buffer. The `FFmpegAudioMixer` maintains separate `SourceBuffer` instances for microphone and system audio, ensuring that timing synchronization occurs before mixing rather than during the RMS computation itself.

### How do I tune the ducking for very quiet speakers?

Decrease the `SPEECH_THRESHOLD` constant 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) below the default 0.01 value, or increase the `system_ducking` attenuation to 0.3 or 0.2 for more aggressive background suppression. For extremely quiet environments, you may also need to adjust your microphone gain upstream before it reaches the Meetily pipeline, as the RMS calculation depends on sufficient signal-to-noise ratio in the input buffer.