# Recording Path vs Transcription Path in Meetily's Audio Pipeline

> Understand the Meetily audio pipeline: the recording path saves all audio as WAV, while the transcription path uses VAD and Whisper for live captioning with speech segments.

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

---

**The recording path captures the full mixed audio (microphone + system) to disk as a WAV file, while the transcription path filters that same mixed audio through Voice Activity Detection (VAD) and sends only speech segments to Whisper at 16 kHz for live captioning.**

Meetily's audio processing engine, located in the [`frontend/src-tauri/src/audio/pipeline.rs`](https://github.com/Zackriya-Solutions/meetily/blob/main/frontend/src-tauri/src/audio/pipeline.rs) file of the [Zackriya-Solutions/meetily](https://github.com/Zackriya-Solutions/meetily) repository, implements a dual-path architecture that processes the same captured audio for two distinct purposes. Understanding the difference between these paths reveals how the application simultaneously produces high-fidelity meeting recordings and real-time transcripts without redundant processing.

## Core Architecture of Meetily's Audio Pipeline

The pipeline begins with **AudioCapture** grabbing synchronized streams from both the microphone and system audio. After optional preprocessing (resampling, noise suppression, and loudness normalization), samples accumulate in an **AudioMixerRingBuffer**. When a 50 ms window is available, `extract_window` returns synchronized buffers that feed both downstream paths.

The critical divergence occurs inside `AudioPipeline::run` after the **ProfessionalAudioMixer** blends the two audio sources. At this point, the mixed buffer travels through two separate channels: one for persistence and one for speech recognition.

## The Recording Path: Capturing the Complete Audio Mix

### Data Flow and File Destination

In the **recording path**, the pipeline forwards the complete mixed window to `recording_sender_for_mixed`. This occurs immediately after mixing, preserving the full audio experience exactly as the user heard it.

The code in [`frontend/src-tauri/src/audio/pipeline.rs`](https://github.com/Zackriya-Solutions/meetily/blob/main/frontend/src-tauri/src/audio/pipeline.rs) (lines 68-78) wraps the mixed buffer in an `AudioChunk` struct:

```rust
if let Some(ref rec_sender) = self.recording_sender_for_mixed {
    let rec_chunk = AudioChunk {
        data: mixed.clone(),
        sample_rate: self.sample_rate,  // Original rate (e.g., 48000 Hz)
        timestamp: chunk.timestamp,
        chunk_id: self.chunk_id_counter,
        device_type: DeviceType::Microphone,
    };
    let _ = rec_sender.send(rec_chunk);
}

```

The **RecordingSaver** (defined in [`recording_saver.rs`](https://github.com/Zackriya-Solutions/meetily/blob/main/recording_saver.rs)) receives these chunks and writes them sequentially to a `.wav` file on disk. Because this path receives every sample from both audio sources, the final recording contains the complete meeting audio without gaps or filtering.

### Configuration and Initialization

The recording path is activated when `AudioPipelineManager::start` receives a `recording_sender` option. Lines 94-97 of [`pipeline.rs`](https://github.com/Zackriya-Solutions/meetily/blob/main/pipeline.rs) establish this connection:

```rust
// In AudioPipelineManager::start
pipeline.recording_sender_for_mixed = recording_sender;

```

If no sender is provided, the recording path remains inactive, allowing the pipeline to run transcription-only mode.

## The Transcription Path: Isolating Speech for Whisper

### VAD Filtering and Resampling

The **transcription path** processes the *same* mixed buffer through **ContinuousVadProcessor** before sending data to the speech recognition engine. This path discards silence and non-speech segments to optimize Whisper's performance and reduce processing overhead.

In [`frontend/src-tauri/src/audio/pipeline.rs`](https://github.com/Zackriya-Solutions/meetily/blob/main/frontend/src-tauri/src/audio/pipeline.rs) (lines 85-108), the transcription logic follows the recording logic:

```rust
// Transcription path (VAD → Whisper)
if let Ok(segments) = self.vad_processor.process_audio(&mixed) {
    for seg in segments {
        let trans_chunk = AudioChunk {
            data: seg.samples,
            sample_rate: 16_000,  // Down-sampled for Whisper
            timestamp: seg.start_timestamp_ms / 1_000.0,
            chunk_id: self.chunk_id_counter,
            device_type: DeviceType::Microphone,
        };
        let _ = self.transcription_sender.send(trans_chunk);
    }
}

```

Notice that the **transcription path** creates new `AudioChunk` objects with **16 kHz sample rate** regardless of the original capture rate. This down-sampling ensures compatibility with Whisper's expected input format while the **recording path** preserves the original sample rate for archival quality.

### Whisper Integration

Chunks emitted on `transcription_sender` travel to the Whisper Engine in [`frontend/src-tauri/src/audio/transcription/engine.rs`](https://github.com/Zackriya-Solutions/meetily/blob/main/frontend/src-tauri/src/audio/transcription/engine.rs). The engine converts these speech-only segments into text and emits Tauri events (`transcript-update`) that the frontend UI consumes for live captions.

## Key Differences Between the Two Paths

| **Recording Path** | **Transcription Path** |
|-------------------|----------------------|
| **Goal**: Archive complete mixed audio for playback | **Goal**: Feed speech-only segments to Whisper for text conversion |
| **Output**: Full WAV file with mic + system audio | **Output**: Live text stream via Tauri events |
| **Sample Rate**: Preserved at original capture rate (e.g., 48000 Hz) | **Sample Rate**: Hard-coded to 16000 Hz for Whisper compatibility |
| **Filtering**: None; receives all audio including silence | **Filtering**: VAD removes non-speech segments before transmission |
| **Destination**: `RecordingSaver` writes to disk | **Destination**: Whisper Engine processes in memory |

## How the Two Paths Work Together

The coordinated flow ensures both paths receive identical source material while serving different consumption patterns:

1. **Capture**: Raw streams enter through [`microphone.rs`](https://github.com/Zackriya-Solutions/meetily/blob/main/microphone.rs) and [`system.rs`](https://github.com/Zackriya-Solutions/meetily/blob/main/system.rs) with synchronized timestamps
2. **Mixing**: `ProfessionalAudioMixer::mix_window` blends buffers without aggressive ducking
3. **Recording Branch**: The mixed buffer clones immediately to `recording_sender_for_mixed`
4. **Transcription Branch**: The same mixed buffer passes through VAD, creating speech-only chunks down-sampled to 16 kHz
5. **Parallel Processing**: Disk I/O and speech recognition occur simultaneously on separate threads

This architecture guarantees that pauses, background noise, and cross-talk appear in the final recording, while only intelligible speech triggers the transcription API—optimizing both accuracy and cost.

## Summary

- **Recording path** in [`frontend/src-tauri/src/audio/pipeline.rs`](https://github.com/Zackriya-Solutions/meetily/blob/main/frontend/src-tauri/src/audio/pipeline.rs) sends complete mixed `AudioChunk` data to `RecordingSaver` for WAV file creation at the original sample rate.
- **Transcription path** processes identical mixed audio through `ContinuousVadProcessor`, sending only speech segments down-sampled to 16 kHz to the Whisper Engine.
- Both paths originate from the same 50 ms window extraction in `AudioPipeline::run`, ensuring temporal alignment between recorded audio and generated transcripts.
- The separation allows Meetily to archive full-fidelity audio while minimizing Whisper processing costs by filtering silence.

## Frequently Asked Questions

### Why does the transcription path use 16 kHz while the recording path preserves the original sample rate?

Whisper models are trained and optimized for 16 kHz audio input. The **transcription path** resamples audio to match this expectation, ensuring maximum transcription accuracy while reducing computational load. The **recording path** preserves the original rate (commonly 48000 Hz) to maintain broadcast-quality audio for playback and post-processing.

### What happens to audio segments that the VAD classifies as non-speech?

Non-speech segments (silence, typing, background noise) are discarded by `ContinuousVadProcessor` and never reach `transcription_sender`. These segments still flow through the **recording path**, so the archived WAV file captures the complete meeting audio including all pauses and environmental sounds.

### Can the recording path operate independently without transcription enabled?

Yes. The `recording_sender_for_mixed` field is an `Option` in the `AudioPipeline` struct. If `AudioPipelineManager::start` receives `None` for the recording sender, only the transcription path executes. Conversely, providing a recording sender without configuring transcription would require initializing the VAD processor, though typically both paths run simultaneously in production.

### Where does the actual file writing occur in the recording path?

The **RecordingSaver** component defined in [`frontend/src-tauri/src/audio/recording_saver.rs`](https://github.com/Zackriya-Solutions/meetily/blob/main/frontend/src-tauri/src/audio/recording_saver.rs) handles the final disk writes. It receives `AudioChunk` objects from the `recording_sender_for_mixed` channel and appends them to the output WAV file, managing headers and finalization when the recording stops.