# How to Optimize Meetily's Audio Pipeline for Low-Latency Recording

> Optimize Meetily's audio pipeline for low-latency recording. Learn techniques to achieve sub-100ms end-to-end latency for real-time applications.

- Repository: [Zackriya Solutions/meetily](https://github.com/Zackriya-Solutions/meetily)
- Tags: performance
- Published: 2026-07-14

---

**Reduce the mixing window to 50ms, reuse a persistent Rubato resampler, compile with `--release` to strip debug logging, tune the VAD redemption time to 200ms, and implement forced buffer flushing to achieve sub-100ms end-to-end latency from microphone capture to transcription.**

Meetily's meeting assistant relies on a real-time audio pipeline built in Rust that processes microphone and system audio through a VAD-driven ring-buffer architecture. To optimize Meetily's audio pipeline for low-latency recording, you must adjust buffer sizes, resampling strategies, and voice activity detection parameters defined in the core audio processing files.

## Core Optimization Strategies

### 1. Minimize the Ring-Buffer Mixing Window

The `AudioMixerRingBuffer` accumulates samples until a fixed window is ready for processing. In [`frontend/src-tauri/src/audio/pipeline.rs`](https://github.com/Zackriya-Solutions/meetily/blob/main/frontend/src-tauri/src/audio/pipeline.rs) (lines 16-36), the default mixing window is set to 600ms, which creates unnecessary delay before audio chunks can be mixed and forwarded.

Reduce the `window_ms` constant to 50ms to minimize the time before a chunk can be mixed. The max buffer of 400ms absorbs device jitter while allowing the smaller processing window:

```rust
// In AudioMixerRingBuffer::new (pipeline.rs:16-36)
let window_ms = 50.0; // Reduced from 600ms for ultra-low latency

```

### 2. Persist the Rubato Resampler Across Chunks

Audio resampling can introduce significant overhead if performed with temporary allocations. Meetily uses a persistent `SincFixedIn` resampler from the Rubato crate that processes fixed-size 512-sample chunks. According to the source code in [`pipeline.rs`](https://github.com/Zackriya-Solutions/meetily/blob/main/pipeline.rs) (lines 24-66), reusing a single resampler instance prevents the "energy amplification" bug and eliminates per-chunk allocation overhead.

Ensure the resampler is created once per device in `AudioCapture::new` and reused throughout the session:

```rust
// Ensure persistent resampler is enabled (pipeline.rs:24-66)
let resampler = if needs_resampling {
    // Keep the same instance across all chunks
    Some(SincFixedIn::<f32>::new(
        TARGET_SAMPLE_RATE as f64 / sample_rate as f64,
        2.0,
        params,
        RESAMPLER_CHUNK_SIZE,
        1,
    )?)
} else { None };

```

### 3. Strip Hot-Path Logging in Production

Debug logging in the hot path can block the real-time thread with I/O overhead. In [`pipeline.rs`](https://github.com/Zackriya-Solutions/meetily/blob/main/pipeline.rs) (lines 86-115), Meetily uses a `perf_debug!` macro that expands to nothing in release builds, and summary logs are throttled to emit only every 200 chunks or 60 seconds.

Always build the Tauri frontend in release mode to eliminate these debug statements entirely:

```bash

# Build with --release so perf_debug! disappears

cargo build --release

```

### 4. Reduce VAD Redemption Time

The VAD (Voice Activity Detection) processor determines when speech segments end. By default, `ContinuousVadProcessor` is instantiated with a `redemption_time` of 400ms in [`pipeline.rs`](https://github.com/Zackriya-Solutions/meetily/blob/main/pipeline.rs) (line 27), meaning the system waits 400ms of silence before finalizing a speech segment.

Reducing this value to 200ms enables faster speech-end detection, allowing the pipeline to deliver segments to Whisper the moment speech stops rather than waiting for a timer-based threshold:

```rust
// Shorten VAD redemption time (pipeline.rs:27)
let redemption_time = 200; // Reduced from 400ms for faster cut-off

```

### 5. Implement Force-Flush on Shutdown

Without proper flushing, buffered audio can remain stuck in the pipeline when recording stops, causing shutdown latencies exceeding 30 seconds. The pipeline handles special "flush" chunks identified by `chunk_id == u64::MAX` to force an immediate VAD flush, as implemented in [`pipeline.rs`](https://github.com/Zackriya-Solutions/meetily/blob/main/pipeline.rs) (lines 70-88).

When stopping a meeting, call `AudioPipelineManager::force_flush_and_stop()` (lines 28-73) to guarantee the pipeline empties its buffers instantly, reducing shutdown latency to under 100ms:

```rust
// Flush immediately when stopping recording
pipeline_manager.force_flush_and_stop().await?;

```

## Key Source Files for Low-Latency Tuning

The following files in the `frontend/src-tauri/src/audio/` directory define the low-latency audio path:

- **[`pipeline.rs`](https://github.com/Zackriya-Solutions/meetily/blob/main/pipeline.rs)** – Core mixing logic, ring-buffer management (`AudioMixerRingBuffer`), persistent resampler implementation, VAD loop, and flush handling (lines 16-115).
- **[`audio_processing.rs`](https://github.com/Zackriya-Solutions/meetily/blob/main/audio_processing.rs)** – Helper functions for mono conversion, high-pass filtering, RNNoise integration, and loudness normalization.
- **[`recording_manager.rs`](https://github.com/Zackriya-Solutions/meetily/blob/main/recording_manager.rs)** – Orchestrates pipeline start/stop and connects the mixed-audio recorder to the output.
- **`devices/`** – Device discovery and platform-specific audio APIs for testing latency across different operating systems.
- **[`vad.rs`](https://github.com/Zackriya-Solutions/meetily/blob/main/vad.rs)** – Implementation of `ContinuousVadProcessor` used for real-time speech segmentation.

## Summary

- **Shrink the mixing window** from 600ms to 50ms in `AudioMixerRingBuffer` to reduce pre-processing delay.
- **Reuse a single `SincFixedIn` resampler** across all chunks to avoid allocation overhead and energy amplification bugs.
- **Build with `--release`** to eliminate `perf_debug!` macros and throttled logging from the hot path.
- **Tune VAD redemption time** from 400ms to 200ms for faster speech-segment delivery.
- **Call `force_flush_and_stop()`** to prevent 30-second shutdown delays by immediately flushing remaining buffers.

## Frequently Asked Questions

### What is the default mixing window size in Meetily?

The default mixing window is **600ms** as defined in [`pipeline.rs`](https://github.com/Zackriya-Solutions/meetily/blob/main/pipeline.rs) (line 28), but the architecture supports reducing this to **50ms** without breaking the ring-buffer logic. The 400ms maximum buffer size provides sufficient headroom for device jitter while the smaller window enables faster processing.

### Why does reusing the resampler reduce latency?

Creating a new resampler for each audio chunk causes memory allocation overhead and a known "energy amplification" bug. By reusing a persistent `SincFixedIn` instance created in `AudioCapture::new` (lines 24-66), the pipeline maintains a deterministic processing path with no per-chunk allocation, keeping the CPU cache warm and predictable.

### How do I completely disable debug logging in the audio pipeline?

The pipeline uses conditional compilation through the `perf_debug!` macro. When you build with `cargo build --release`, these macros expand to nothing, completely removing debug logging from the hot path. Additionally, summary logs are automatically throttled to emit only every 200 chunks or 60 seconds to minimize I/O blocking.

### What causes the 30-second shutdown delay in Meetily?

The delay occurs when the VAD buffer retains unprocessed audio samples waiting for silence detection to finalize segments. By implementing the force-flush mechanism using `chunk_id == u64::MAX` (lines 70-88) and calling `AudioPipelineManager::force_flush_and_stop()`, you force immediate VAD finalization, reducing shutdown latency from over 30 seconds to under 100 milliseconds.