Meetily Audio Ring Buffer and Windowed Processing for Synchronized Streams Explained

Meetily uses a VecDeque-based ring buffer architecture in frontend/src-tauri/src/audio/pipeline.rs to synchronize microphone and system audio streams into fixed 50 ms windows, applying zero-padding when sources lag and soft-scaling mixing to prevent clipping while maintaining phase alignment.

The open-source Meetily application (Zackriya-Solutions/meetily) captures two independent audio sources—user microphone input and system output—to produce unified recordings for transcription. To handle the asynchronous nature of these streams without drift or echo, the codebase implements a sophisticated audio ring buffer and windowed processing system that aligns samples before mixing.

How the Ring Buffer Works

The AudioMixerRingBuffer struct defined at lines 16‑23 in frontend/src-tauri/src/audio/pipeline.rs serves as the synchronization core. It maintains two separate queues: mic_buffer and system_buffer, both implemented as VecDeque<f32> collections optimized for O(1) push/pop operations at both ends.

Buffer Architecture and Capacity Management

Each buffer initializes with a capacity calculated from the device sample rate. The system defines a fixed window_size_samples representing approximately 50 ms of audio (calculated as (sample_rate as f32 * 600.0 / 1000.0) as usize), while the max_buffer_size acts as a safety limit set to eight times the window size (approximately 400 ms). This configuration appears in the constructor at lines 25‑47:

// Ring buffer initialization from pipeline.rs
let window_size = (sample_rate as f32 * 600.0 / 1000.0) as usize;
let max_buffer = window_size * 8; // ~400ms safety margin

Samples arrive asynchronously via the add_samples method (lines 49‑65), which pushes incoming f32 values into the respective queue based on the DeviceType enum (Microphone or System). The generous 400 ms capacity absorbs temporary stalls common in Bluetooth microphones or virtual audio devices on macOS.

Handling Jitter and Overflow Protection

When one stream lags significantly, the ring buffer prevents unbounded memory growth. At lines 67‑74, the implementation checks if either queue exceeds max_buffer_size; if so, it drops older samples and emits a warning. This safeguards against Core Audio delays while maintaining real-time processing constraints.

Windowed Processing and Synchronization

The windowed approach ensures that microphone and system audio remain phase-aligned before mixing. Rather than processing sample-by-sample, the system waits until both buffers contain at least one full window of data.

Extracting Aligned Windows

The can_mix method returns true only when both queues contain window_size_samples or more. At this point, extract_window (lines 92‑141) pulls synchronized slices from each buffer. This alignment prevents the echo and phase drift that would otherwise occur if the streams were processed independently.

Zero-Padding Strategy

When one stream provides fewer samples than the window requires—common during initialization or stream interruptions—the extraction method applies zero-padding rather than last-sample hold. Lines 100‑118 handle microphone zero-padding, while lines 120‑138 handle system audio. This choice prevents audible artifacts, as silence is imperceptible at the 48 kHz pipeline rate, whereas repeated samples would create noticeable distortion.

// Example: Extracting windows with potential zero-padding
if ring.can_mix() {
    if let Some((mic_window, sys_window)) = ring.extract_window() {
        // mic_window and sys_window are both exactly window_size_samples long
        // Short streams have been zero-padded automatically
    }
}

The Mixing Pipeline

Once synchronized windows are extracted, the ProfessionalAudioMixer::mix_window method (referenced at lines 61‑84) processes the aligned audio. The mixer applies a soft-scaling algorithm that prevents clipping while preserving the microphone's normalized loudness levels. This ensures that system audio (music, notifications) does not overpower the primary speech signal.

Output Routing

The mixed output follows two paths:

  1. Transcription Pipeline: The audio feeds into the VAD (Voice Activity Detection) system for real-time transcription
  2. Recording Pipeline: The recording_sender_for_mixed channel writes the final WAV file via recording_manager.rs

This dual-output design allows Meetily to simultaneously generate clean recordings and provide accurate transcription without processing the streams twice.

Why This Design Matters

The ring buffer and windowed processing architecture provides three critical advantages for synchronized audio capture:

  • Phase Synchronization: By waiting for aligned windows, the system guarantees that microphone and system audio remain in phase, eliminating echo and temporal misalignment
  • Jitter Robustness: The 400 ms max_buffer_size accommodates temporary stalls in either stream, crucial for handling Bluetooth latency and virtual audio devices
  • CPU Efficiency: The VecDeque structure provides constant-time operations, while the fixed window size limits per-cycle processing load, maintaining real-time performance even on resource-constrained systems

Summary

  • Meetily implements an audio ring buffer in frontend/src-tauri/src/audio/pipeline.rs using VecDeque<f32> queues for microphone and system audio
  • The windowed processing system operates on fixed 50 ms windows, with a 400 ms safety buffer to handle stream jitter
  • Zero-padding (not sample-hold) fills short windows to prevent artifacts, implemented in the extract_window method
  • The ProfessionalAudioMixer::mix_window applies soft-scaling to prevent clipping while preserving speech loudness
  • This architecture ensures phase-aligned, low-latency mixing suitable for both high-quality recording and real-time transcription pipelines

Frequently Asked Questions

How does the ring buffer handle when one audio stream is delayed?

The system uses a max_buffer_size of approximately 400 ms (eight times the window size) to absorb temporary delays. If one stream falls behind, the other buffer accumulates samples up to this limit. Once the delayed stream catches up, the extract_window method can pull aligned data. If the delay exceeds 400 ms, older samples are dropped from the leading buffer to prevent memory overflow, with a warning logged at lines 67‑74.

Why does Meetily use zero-padding instead of repeating the last sample?

Zero-padding is chosen over last-sample hold to avoid audible artifacts. When a buffer is shorter than the window size, filling the remainder with zeros creates silence that is imperceptible at 48 kHz, whereas repeating the last sample would generate noticeable distortion or clicking sounds. This logic is implemented separately for microphone (lines 100‑118) and system audio (lines 120‑138) in the extract_window method.

What prevents the mixed audio from clipping when combining two sources?

The ProfessionalAudioMixer::mix_window method applies a soft-scaling algorithm that normalizes the combined amplitude of both streams. This prevents digital clipping while maintaining the relative loudness levels, ensuring that system audio (such as music or notifications) does not overpower the primary microphone speech signal.

Where is the ring buffer initialized in the Meetily codebase?

The AudioMixerRingBuffer struct is defined at lines 16‑23 of frontend/src-tauri/src/audio/pipeline.rs, with its constructor handling capacity calculations at lines 25‑47. The struct is instantiated with the device sample rate (typically 48 kHz), from which it calculates the window_size_samples and max_buffer_size parameters.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →