Meetily Recording Path and Transcription Path: Understanding the Difference
Meetily processes captured audio through two distinct pipelines: the recording path writes full mixed microphone and system audio to a WAV file at the original sample rate, while the transcription path filters the same mixed audio through Voice Activity Detection (VAD), extracts only speech segments, and downsamples them to 16 kHz for real-time Whisper processing.
Meetily is an open-source meeting assistant built by Zackriya-Solutions that captures both microphone and system audio to create complete meeting records. Understanding the distinction between the recording path and transcription path is crucial for developers customizing audio processing or debugging transcription accuracy. Both paths originate in frontend/src-tauri/src/audio/pipeline.rs, sharing the initial capture and mixing stages before diverging to serve different end goals—one preserves the full audio experience while the other optimizes for speech recognition.
The Shared Foundation: Audio Capture and Mixing
Both paths begin in the unified audio pipeline implemented in frontend/src-tauri/src/audio/pipeline.rs. The AudioCapture module grabs raw streams from the microphone and system audio, applying noise suppression, high-pass filtering, and loudness normalization to the microphone input.
These synchronized streams accumulate in an AudioMixerRingBuffer. When a 50 ms window is available, the extract_window method returns two synchronized buffers that are blended by ProfessionalAudioMixer::mix_window. This mixing occurs without aggressive ducking—the microphone audio remains normalized while system audio stays at natural levels.
After mixing, the pipeline splits into two parallel processing routes.
The Recording Path: Full-Fidelity Archival
The recording path preserves exactly what the user hears by writing the complete mixed audio stream to disk.
Once the mixed window is built, the pipeline checks for the presence of recording_sender_for_mixed. If configured, it wraps the mixed buffer in an AudioChunk struct and emits it via the recording sender. According to the Meetily source code, this occurs at lines 68-78 of pipeline.rs.
The AudioChunk sent to the recording path maintains the original sample_rate (typically 48 kHz) and includes metadata identifying it as mixed microphone audio. The RecordingSaver component (defined in frontend/src-tauri/src/audio/recording_saver.rs) receives these chunks and appends them directly to the output WAV file.
Key characteristics of the recording path:
- Retains all audio content, including silence, background noise, and system sounds
- Uses the original capture sample rate for maximum quality
- Writes sequentially to disk via
recording_sender_for_mixed.send(recording_chunk)
The Transcription Path: Optimized Speech Processing
The transcription path optimizes for Whisper speech recognition by stripping non-speech segments and standardizing the audio format.
Voice Activity Detection (VAD) Processing
After mixing, the same mixed buffer passes through ContinuousVadProcessor (implemented in frontend/src-tauri/src/audio/vad.rs). This processor identifies speech segments and discards silence, significantly reducing the data volume sent to the transcription engine.
The VAD processing and subsequent emission to the transcription sender occurs at lines 85-108 of pipeline.rs. Each detected speech segment is extracted with its precise timestamp, creating discrete chunks of speech-only audio.
Downsampling for Whisper Optimization
Unlike the recording path, transcription chunks undergo resampling. The pipeline creates new AudioChunk instances (lines 44-50) containing the speech segment samples downsampled to 16 kHz, which is the optimal input rate for OpenAI's Whisper model.
These chunks travel via transcription_sender to the engine defined in frontend/src-tauri/src/audio/transcription/engine.rs. The engine emits Tauri events (transcript-update) that the frontend consumes to display live captions.
Side-by-Side Comparison
| Aspect | Recording Path | Transcription Path |
|---|---|---|
| Primary Goal | Archive complete mixed audio for playback | Extract speech for real-time transcription |
| Processing Stage | Direct output from mixer | Mixer output → VAD filter |
| Audio Content | Full mixed stream (mic + system) | Speech-only segments |
| Sample Rate | Original (e.g., 48 kHz) | Downsampled to 16 kHz |
| Sender Channel | recording_sender_for_mixed |
transcription_sender |
| Destination | RecordingSaver → .wav file |
Whisper Engine → UI text |
| Code Location | pipeline.rs lines 68-78 |
pipeline.rs lines 85-108 |
Implementation in the Codebase
The wiring of both paths occurs during pipeline initialization in AudioPipelineManager::start (lines 94-97 of pipeline.rs). Here, the manager optionally sets the recording sender while always configuring the transcription sender.
// Starting the pipeline (recording + transcription)
let mut manager = AudioPipelineManager::new();
manager.start(
recording_state.clone(),
transcription_sender.clone(),
/*target_chunk_duration_ms=*/ 0,
/*sample_rate=*/ 48000,
Some(recording_sender.clone()), // <- enables recording path
mic_name.clone(),
mic_kind,
system_name.clone(),
system_kind,
)?;
Inside the AudioPipeline::run method, the divergence is explicit:
// Inside AudioPipeline::run – the two diverging paths
if let Some((mic_win, sys_win)) = self.ring_buffer.extract_window() {
// 1️⃣ Mix the windows
let mixed = self.mixer.mix_window(&mic_win, &sys_win);
// ──► Recording path (writes to .wav)
if let Some(ref rec_sender) = self.recording_sender_for_mixed {
let rec_chunk = AudioChunk {
data: mixed.clone(),
sample_rate: self.sample_rate,
timestamp: chunk.timestamp,
chunk_id: self.chunk_id_counter,
device_type: DeviceType::Microphone,
};
let _ = rec_sender.send(rec_chunk);
}
// ──► 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,
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);
}
}
}
Supporting files that define this behavior include:
frontend/src-tauri/src/audio/recording_manager.rs– Orchestrates pipeline start/stopfrontend/src-tauri/src/audio/recording_saver.rs– Handles WAV file writingfrontend/src-tauri/src/audio/vad.rs– Implements speech detectionfrontend/src-tauri/src/audio/transcription/engine.rs– Interfaces with Whisperfrontend/src-tauri/src/audio/capture/microphone.rsandsystem.rs– Raw audio capture sources
Summary
- Shared Origin: Both paths start from the same mixed audio window in
frontend/src-tauri/src/audio/pipeline.rs, ensuring synchronization between recording and transcription. - Recording Path: Writes full-fidelity mixed audio at the original sample rate to a WAV file via
recording_sender_for_mixed, preserving the complete meeting audio including non-speech segments. - Transcription Path: Filters mixed audio through
ContinuousVadProcessorto extract speech, downsamples to 16 kHz, and streams to Whisper viatranscription_senderfor live captioning. - Efficiency: By separating archival quality from transcription optimization, Meetily avoids redundant processing while maintaining high accuracy in both domains.
Frequently Asked Questions
Why does Meetily use two separate paths instead of transcribing the recorded WAV file later?
Meetily processes transcription in real-time to provide live captions during meetings. The transcription path operates on a continuous stream with VAD filtering and downsampling to minimize latency and CPU usage, whereas the recording path maintains full quality for the final archive. Transcribing the high-fidelity WAV file afterward would introduce significant delays and require re-processing the entire audio stream.
What happens to audio that the VAD filters out in the transcription path?
Non-speech audio detected by ContinuousVadProcessor is simply discarded and never sent to the Whisper engine. However, this audio is still preserved in the recording path, ensuring that background sounds, silence, and system audio remain available in the final WAV file even if they do not appear in the transcript.
Can I disable the recording path while keeping transcription active?
Yes. The recording path is optional and controlled by the presence of recording_sender_for_mixed. When initializing the pipeline via AudioPipelineManager::start, passing None for the recording sender (or omitting it) disables the recording functionality while allowing the transcription path to continue operating normally via transcription_sender.
Why is the transcription path limited to 16 kHz when the recording uses 48 kHz?
OpenAI's Whisper model is trained and optimized for 16 kHz audio input. Down-sampling from 48 kHz to 16 kHz in the transcription path reduces data volume by 66% without losing speech recognition accuracy, significantly improving processing speed and reducing memory usage. The recording path retains 48 kHz to preserve audio quality for human listeners.
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:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →