How to Debug Audio Capture Issues on macOS, Windows, and Linux for Meetily
To debug audio capture issues in Meetily, enable Rust debug logging, check platform-specific device permissions, verify virtual audio devices are recognized, and monitor the AudioMixerRingBuffer for overflow warnings that indicate sample-rate mismatches or jitter.
Meetily captures two audio streams—microphone input and system audio output—through a Rust-based Tauri core located in frontend/src-tauri/src/audio/. Understanding how to debug audio capture issues on macOS, Windows, and Linux for Meetily requires familiarity with the platform-specific device enumeration modules and the unified mixing pipeline that processes both streams before Whisper transcription.
Understanding Meetily’s Audio Architecture
The capture pipeline routes audio from device-specific backends through a series of processing stages before reaching the transcription engine.
Core Processing Components
- AudioCapture – Handles device-specific capture, mono conversion, optional resampling, noise suppression, high-pass filtering, and EBU-R128 loudness normalization (microphone only).
- AudioMixerRingBuffer – Maintains separate FIFO buffers for mic and system audio, aligns them into 50 ms windows, and emits warnings on overflow.
- ProfessionalAudioMixer – Performs soft-clipping mixing that scales the system stream to approximately 70% to prevent hard clipping.
- AudioPipeline – Receives mixed windows, runs the voice-activity detector (VAD), forwards speech segments to Whisper, and streams mixed audio to the WAV recorder.
Platform-Specific Backends
While the mixing logic remains cross-platform, device discovery differs by operating system:
- macOS – Uses
audio/devices/platform/macos.rswith CoreAudio and ScreenCaptureKit APIs. - Windows – Uses
audio/devices/platform/windows.rswith WASAPI for loopback capture. - Linux – Uses
audio/devices/platform/linux.rswith ALSA/PulseAudio fallback logic.
Common Audio Capture Failure Modes
Recognizing symptom patterns helps isolate whether the issue stems from permissions, device enumeration, or signal processing.
| Symptom | Likely Cause | Investigation Location |
|---|---|---|
| No microphone data – silent transcript | Permission denied, wrong input device, or device not found | AudioCapture.handle_stream_error and device list in audio/devices/discovery.rs |
| System audio missing – only mic heard | Virtual audio device not installed or filtered out by should_include_output_device |
audio/devices/platform/macos.rs (device filtering logic) |
| Audio garbled or speed-up | Sample-rate mismatch on Bluetooth or low-rate devices | AudioCapture.needs_resampling and persistent resampler in pipeline.rs |
| Choppy or distorted mix | Ring-buffer overflow or jitter on system stream | AudioMixerRingBuffer.add_samples warnings and max_buffer_size calculations |
| Transcription never fires | VAD not detecting speech due to high redemption time or wrong sample-rate | AudioPipeline::run and ContinuousVadProcessor::new initialization |
Platform-Specific Debugging Steps
macOS
-
Verify permissions – Both Microphone and Screen Recording permissions must be granted in System Settings. Errors surface in
AudioCapture.handle_stream_errorasPermissionDenied. -
Check virtual audio devices – System capture requires a virtual loopback device such as BlackHole. The macOS device filter in
should_include_output_deviceexcludes names containing "speakers" but should list BlackHole. -
Monitor ring-buffer health – Run with debug logging enabled:
RUST_LOG=app_lib::audio=debug ./clean_run.shLook for buffer status messages:
📊 Ring buffer status: mic=1234 samples, sys=5678 samples (max=9600) ⚠️ Microphone buffer overflow …Frequent overflow indicates jitter; increase
max_buffer_sizeinAudioMixerRingBuffer::newor check CPU load. -
Inspect resampling – Bluetooth headsets often report 16 kHz. If you see:
⚠️ SAMPLE RATE MISMATCH DETECTED …The persistent resampler in
AudioCapturewill convert to 48 kHz. Absence of this warning suggests the device already reports the correct rate.
Windows
-
Enumerate devices – Open the Audio → Device panel in the UI. The list is built by
audio/devices/platform/windows.rs. Missing devices indicate WASAPI subsystem issues, often driver-related. -
Configure loopback capture – System audio uses WASAPI loopback. Ensure the output device is not a "speakers" entry filtered by the same exclusion logic used on macOS.
-
Address latency – Windows exclusive mode can introduce high latency. Reduce logging verbosity and watch for buffer overflow warnings identical to the macOS debugging procedure.
Linux
-
Verify PulseAudio/ALSA – Device discovery in
audio/devices/platform/linux.rsprefers PulseAudio, falling back to ALSA. Run:pactl list short sources pactl list short sinksEnsure expected devices appear in the output.
-
Check user permissions – The binary must run as a user in the
audiogroup. Running withsudois only recommended for testing; otherwise, expectPermissionDeniederrors in logs. -
Manage jitter – Linux system capture often exhibits larger jitter, triggering ring-buffer overflow warnings more frequently. Temporarily raise
window_msinAudioMixerRingBuffer::new(line 28) to 100 ms for debugging purposes.
Diagnostic Commands and Tools
Environment Variable Diagnostics
Enable periodic buffer health reporting:
export MEETILY_AUDIO_DIAGNOSTICS=1
RUST_LOG=debug ./clean_run.sh
Expected output:
🔊 Diagnostics: mic_buffer=3420, sys_buffer=4000, sample_rate=48000
These values confirm whether buffers stay within the configured max_buffer_size (approximately 400 ms).
Tauri Frontend Commands
Trigger device refresh from the developer console:
await invoke('refresh_audio_devices');
Capture a single chunk to verify microphone levels:
let chunk = await invoke('capture_one_chunk', { device_type: 'Microphone' });
let rms = Math.sqrt(chunk.data.reduce((s, x) => s + x*x, 0) / chunk.data.length);
console.log('Microphone RMS:', rms);
Force flush a hung pipeline:
await invoke('force_flush_and_stop');
Rust Debug Logging
Print ring-buffer status from a debug build:
debug!("Ring buffer status: mic={} sys={}", self.mic_buffer.len(), self.system_buffer.len());
Key Source Files for Debugging
| Component | File Path | Purpose |
|---|---|---|
| Device enumeration (macOS) | frontend/src-tauri/src/audio/devices/platform/macos.rs |
Lists input/output devices, filters built-in speakers |
| Device enumeration (Windows) | frontend/src-tauri/src/audio/devices/platform/windows.rs |
WASAPI implementation and loopback capture |
| Device enumeration (Linux) | frontend/src-tauri/src/audio/devices/platform/linux.rs |
ALSA/PulseAudio discovery |
| Device discovery helper | frontend/src-tauri/src/audio/devices/discovery.rs |
Cross-platform device listing for UI |
| Ring-buffer & mixing | frontend/src-tauri/src/audio/pipeline.rs |
AudioMixerRingBuffer, ProfessionalAudioMixer, overflow handling |
| Capture & processing | frontend/src-tauri/src/audio/pipeline.rs |
AudioCapture struct, resampling, noise suppression |
| VAD & pipeline flow | frontend/src-tauri/src/audio/pipeline.rs |
AudioPipeline impl, ContinuousVadProcessor |
| Pipeline management | frontend/src-tauri/src/audio/pipeline.rs |
AudioPipelineManager, force_flush_and_stop (lines 28-66) |
| Diagnostics utility | frontend/src-tauri/src/audio/diagnostics.rs |
Optional periodic logging of buffer health |
Summary
- Enable debug logging with
RUST_LOG=app_lib::audio=debugto expose buffer overflows, permission errors, and sample-rate mismatches. - Verify platform permissions: macOS requires Screen Recording and Microphone access; Linux requires membership in the
audiogroup; Windows requires functional WASAPI drivers. - Check virtual devices on macOS (BlackHole) and ensure system audio devices aren't filtered by
should_include_output_device. - Monitor AudioMixerRingBuffer for overflow warnings that indicate jitter or mismatched sample rates between microphone and system streams.
- Use force flush (
force_flush_and_stop) to recover from pipeline hangs without restarting the application.
Frequently Asked Questions
Why is my microphone working but system audio is missing?
Missing system audio usually indicates the virtual loopback device is not installed or has been filtered out. On macOS, verify BlackHole is installed and appears in the device list; the macOS implementation in audio/devices/platform/macos.rs filters out devices with "speakers" in the name, so BlackHole must be explicitly selected. On Windows, ensure the WASAPI loopback device is not disabled, and on Linux, verify the PulseAudio monitor source exists with pactl list.
How do I fix choppy or distorted audio in Meetily?
Choppy audio typically results from AudioMixerRingBuffer overflow, visible in logs as warnings from add_samples. This occurs when the system audio stream jitters or when CPU load prevents timely processing. Increase max_buffer_size in AudioMixerRingBuffer::new or reduce the redemption time in ContinuousVadProcessor::new to decrease latency.
What causes the "SAMPLE RATE MISMATCH" warning?
This warning appears in AudioCapture when a device reports a sample rate other than 48 kHz, such as 16 kHz Bluetooth headsets. The persistent resampler in pipeline.rs automatically converts the stream, but the warning confirms the conversion is active. If the warning is absent despite using a Bluetooth device, the device may be inadvertently reporting 48 kHz through system interpolation.
How do I force stop the audio pipeline if the app hangs?
If the application hangs during shutdown, the pipeline is likely waiting for a flush signal. Trigger the Tauri command force_flush_and_stop, which sends three special AudioChunk structures with chunk_id = u64::MAX to force the AudioPipeline to terminate immediately. This command is available via the UI "Force stop" button or programmatically through await invoke('force_flush_and_stop').
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 →