# Audio Chunk Size and Sample Rate Constraints in Meetily: Technical Specifications

> Discover Meetily's audio chunk size and sample rate constraints. Learn about the 48kHz pipeline, 600ms mixing windows, and 50ms transcription chunks for optimal performance.

- Repository: [Zackriya Solutions/meetily](https://github.com/Zackriya-Solutions/meetily)
- Tags: technical-specifications
- Published: 2026-07-15

---

**Meetily enforces a strict 48 kHz pipeline sample rate with 600 ms mixing windows and transcription chunks of at least 800 samples (≈50 ms at 16 kHz), automatically resampling device inputs via 512-sample blocks to ensure compatibility across all platforms.**

Meetily's audio pipeline is built around strict constraints that stabilize the capture-mix-transcribe flow across diverse hardware. The Zackriya-Solutions/meetily repository implements specific buffer sizes, sample rate conversions, and chunk duration thresholds in its Rust-based audio engine to maintain low latency while ensuring high-quality transcription. Understanding these constraints is essential for developers integrating custom audio sources or debugging performance issues in the Tauri-based frontend.

## Pipeline Sample Rate: The 48 kHz Standard

The **capture sample rate** constraint requires all devices to run at **48 kHz**, referred to as the "pipeline sample rate." In [`frontend/src-tauri/src/audio/audio_v2/recorder.rs`](https://github.com/Zackriya-Solutions/meetily/blob/main/frontend/src-tauri/src/audio/audio_v2/recorder.rs), the `AudioCapture::new` constructor detects any mismatch between the device-reported rate and the 48 kHz target.

When a mismatch is detected, the system creates a **persistent Rubato resampler** that converts any incoming rate to 48 kHz. This resampler is only omitted when the device already reports exactly 48 kHz. This constraint exists because Whisper-VAD expects 48 kHz input, and the mixing, RMS-based ducking, and loudness normalization are all calibrated for this rate.

The resampling target is enforced in `process_audio_data` (lines 99–108) and during the `resampler` initialization (lines 20–31), guaranteeing a single, predictable sample rate downstream and avoiding speed-up/slow-down bugs common with Bluetooth or 44.1 kHz devices.

## Buffer and Window Constraints

The mixing stage operates on fixed **600 ms windows**, which equals approximately **28,800 samples at 48 kHz**. In [`frontend/src-tauri/src/audio/pipeline.rs`](https://github.com/Zackriya-Solutions/meetily/blob/main/frontend/src-tauri/src/audio/pipeline.rs), the `AudioMixerRingBuffer::new` function computes `window_size_samples` from the constant `window_ms = 600.0`.

To absorb jitter from flaky system-audio streams (such as macOS ScreenCaptureKit), the ring buffer capacity is set to **8 times the window size** (approximately 4.8 seconds or 230,400 samples). This is calculated as `max_buffer_size = window_size_samples * 8` at line 35, preventing overflow during temporary latency spikes while maintaining smooth professional mixing.

## Chunk Size Constraints for Processing

### Resampler Block Size

The resampler processes audio in fixed **512-sample blocks**, defined by the constant `RESAMPLER_CHUNK_SIZE = 512` at line 18. The `process_audio_data` function contains a while-loop that drains the `resampler_input_buffer` only when a full 512-sample block is available. This balances latency and quality, buffering partial blocks until enough data is ready for energy-preserving conversion without artifacts.

### Transcription Threshold

For transcription, the VAD-driven pipeline only forwards speech segments longer than **800 samples at 16 kHz** (approximately **50 ms**). In `AudioPipeline::run` at line 40, the system checks `segment.samples.len() >= 800` before sending audio to Whisper. This matches Whisper’s practical lower bound and discards sub-speech fragments that would degrade transcription quality.

### Legacy Constants

The codebase still contains legacy chunk constants in [`frontend/src-tauri/src/lib_old_complex.rs`](https://github.com/Zackriya-Solutions/meetily/blob/main/frontend/src-tauri/src/lib_old_complex.rs): `CHUNK_DURATION_MS = 30_000` (30 seconds) and `MIN_CHUNK_DURATION_MS = 2_000` (2 seconds). These are kept for backward compatibility but are unused by the current VAD-driven pipeline, which replaced the fixed-duration approach with voice-activity detection.

## Implementation Examples

The following Rust code demonstrates how the 48 kHz constraint is enforced during capture initialization:

```rust
// Initialise a capture – the constructor will log a warning and set up a resampler
let capture = AudioCapture::new(
    device.clone(),
    recording_state.clone(),
    device.sample_rate(),   // e.g. 44100 Hz from a Bluetooth mic
    device.channels(),
    DeviceType::Microphone,
    Some(sender),
);
// The capture will automatically resample to 48 kHz before forwarding data.

```

The mixing window calculations are implemented as follows:

```rust
// Mixing window size (600 ms) calculated from the sample rate:
let window_ms = 600.0;
let window_samples = (sample_rate as f32 * window_ms / 1000.0) as usize;
// The ring buffer can hold up to 8× this size:
let max_buffer = window_samples * 8;

```

The VAD-driven pipeline applies the minimum chunk size constraint before transcription:

```rust
// VAD-driven pipeline only forwards speech segments longer than 800 samples @16kHz
if segment.samples.len() >= 800 {
    // send to Whisper
}

```

## Summary

- **All audio entering the mixing stage is at 48 kHz** – the system automatically resamples any device that reports a different rate using a persistent Rubato resampler.
- **Mixing operates on 600 ms windows**, and the ring buffer can hold up to approximately 4.8 seconds of audio to survive jitter from system audio streams.
- **Transcription only receives VAD-filtered chunks ≥ 50 ms** (800 samples at 16 kHz), discarding sub-speech fragments that would degrade recognition accuracy.
- The **resampler works on 512-sample blocks**, buffering partial blocks until enough data is available to ensure energy-preserving conversion without artifacts.

## Frequently Asked Questions

### What sample rate does Meetily require for audio input devices?

Meetily requires all audio input devices to operate at **48 kHz** for the pipeline mixing stage. While the system can accept devices running at other sample rates (such as 44.1 kHz from Bluetooth microphones), it automatically converts them to 48 kHz using a persistent Rubato resampler before the audio reaches the mixing buffer.

### How does Meetily handle audio jitter from system capture sources?

Meetily implements an **8× window buffer** (approximately 4.8 seconds at 48 kHz) in the `AudioMixerRingBuffer` to absorb jitter from flaky system-audio streams like macOS ScreenCaptureKit. This 230,400-sample capacity prevents overflow during temporary latency spikes while maintaining the 600 ms mixing window for professional audio processing.

### What is the minimum audio chunk size sent to the Whisper transcription engine?

The minimum chunk size sent to transcription is **800 samples at 16 kHz**, which equals approximately **50 milliseconds** of audio. The `AudioPipeline::run` function filters out any VAD segments shorter than this threshold to match Whisper's practical lower bound and avoid processing sub-speech fragments.

### Why does Meetily use 512-sample blocks for resampling?

The resampler uses **512-sample blocks** (`RESAMPLER_CHUNK_SIZE`) to balance processing latency with conversion quality. This fixed block size allows the Rubato resampler to accumulate smaller device-specific chunks until a full block is ready, ensuring energy-preserving sample rate conversion without introducing audio artifacts or pitch shifts.