Recording Path vs Transcription Path in Meetily's Audio Pipeline

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 file of the 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 (lines 68-78) wraps the mixed buffer in an AudioChunk struct:

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) 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 establish this connection:

// 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 (lines 85-108), the transcription logic follows the recording logic:

// 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. 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 and 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 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 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.

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 →