Meetily Platform-Specific Audio Device Implementations: Windows, macOS, and Linux Code Breakdown
Meetily implements distinct audio device handlers for Windows, macOS, and Linux in frontend/src-tauri/src/audio/devices/platform/, using WASAPI, ScreenCaptureKit + CoreAudio, and ALSA/PulseAudio respectively.
The meetily open-source meeting assistant handles audio device enumeration and configuration through a carefully structured platform abstraction layer. Located at frontend/src-tauri/src/audio/devices/platform/, this module isolates operating-system-specific code behind a unified Rust interface, enabling consistent cross-platform audio capture while leveraging native APIs for optimal performance.
Platform-Specific Implementation Files
Each operating system receives its own dedicated implementation file with targeted responsibilities:
Windows: WASAPI Integration
The windows.rs file implements audio device handling through WASAPI via the cpal crate. Key capabilities include:
- Enumerating input, output, and loopback devices
- Falling back to the default host if WASAPI enumeration fails
- Providing
get_windows_device()to resolve device names tocpal::Deviceinstances with compatible stream configurations
The Windows implementation prioritizes F32 stereo formats, then any F32 format, then the first available configuration.
File location: frontend/src-tauri/src/audio/devices/platform/windows.rs
macOS: ScreenCaptureKit and CoreAudio
The macos.rs file combines ScreenCaptureKit with CoreAudio (accessed through cpal):
- Lists input devices using the default host
- Filters built-in speakers from output devices while preserving Bluetooth peripherals
- Uses ScreenCaptureKit directly for system-audio capture, bypassing
cpalfor output streams
This dual-API approach addresses macOS-specific requirements for system audio access, which requires entitlements and specialized capture APIs unavailable through standard audio frameworks.
File location: frontend/src-tauri/src/audio/devices/platform/macos.rs
Linux: ALSA and PulseAudio Support
The linux.rs file implements ALSA and PulseAudio support via cpal:
- Enumerates regular input devices through ALSA
- Detects PulseAudio monitor sources to expose system audio as output devices
- Names detected system audio devices as
"<source> (System Audio)"for clear UI presentation
This implementation ensures compatibility with both legacy ALSA configurations and modern PulseAudio-based distributions.
File location: frontend/src-tauri/src/audio/devices/platform/linux.rs
Module Re-Export Pattern
The mod.rs file provides conditional compilation to expose the correct platform implementation:
// frontend/src-tauri/src/audio/devices/platform/mod.rs
#[cfg(target_os = "windows")]
pub use windows::{configure_windows_audio, get_windows_device};
#[cfg(target_os = "macos")]
pub use macos::configure_macos_audio;
#[cfg(target_os = "linux")]
pub use linux::configure_linux_audio;
This pattern allows higher-level code to import platform functions without knowing the target OS at the call site.
Integration with Device Discovery
Platform functions are invoked by audio/devices/discovery.rs to populate the AudioDevice struct list. The rest of the codebase treats devices uniformly through this struct:
AudioDevice { name, device_type }— defined inaudio/devices/configuration.rs- Platform modules handle all native enumeration and stream configuration details
Practical Code Examples
Enumerating Devices in Rust
use crate::audio::devices::platform::*;
pub async fn list_audio_devices(host: &cpal::Host) -> anyhow::Result<Vec<AudioDevice>> {
#[cfg(target_os = "windows")]
let devices = configure_windows_audio(host)?;
#[cfg(target_os = "macos")]
let devices = configure_macos_audio(host)?;
#[cfg(target_os = "linux")]
let devices = configure_linux_audio(host)?;
Ok(devices)
}
Resolving a Device to Stream (Windows)
// Assume `selected` is an AudioDevice chosen by the user
let (device, config) = get_windows_device(&selected)?;
// Now `device` and `config` can be passed to cpal's stream builder
Frontend TypeScript Integration
import { invoke } from '@tauri-apps/api/tauri';
async function fetchAudioDevices() {
const devices = await invoke<AudioDevice[]>('list_audio_devices');
// Populate UI selector with device.name and device.device_type
}
Key Architectural Files
| Purpose | Path |
|---|---|
| Windows implementation | frontend/src-tauri/src/audio/devices/platform/windows.rs |
| macOS implementation | frontend/src-tauri/src/audio/devices/platform/macos.rs |
| Linux implementation | frontend/src-tauri/src/audio/devices/platform/linux.rs |
| Module re-export | frontend/src-tauri/src/audio/devices/platform/mod.rs |
| Device configuration struct | frontend/src-tauri/src/audio/devices/configuration.rs |
| Discovery entry point | frontend/src-tauri/src/audio/devices/discovery.rs |
Summary
- Windows uses WASAPI for full device enumeration including loopback capture, with
get_windows_device()providing detailed stream configuration - macOS combines CoreAudio for input with ScreenCaptureKit for system audio, filtering built-in speakers from outputs
- Linux detects PulseAudio monitor sources to expose system audio alongside ALSA input devices
- All three implementations expose consistent functions through conditional compilation in
mod.rs, enabling uniform treatment in the discovery layer - The abstraction allows the frontend to receive a simple
AudioDevicelist while native APIs handle platform-specific requirements
Frequently Asked Questions
How does Meetily handle system audio capture differently across platforms?
Windows captures system audio through WASAPI loopback devices, which appear as standard output devices in enumeration. MacOS requires ScreenCaptureKit for system audio, as CoreAudio does not provide native loopback support—this bypasses cpal entirely for output streams. Linux uses PulseAudio monitor sources, exposing them as named output devices through the standard enumeration flow.
Why does the macOS implementation filter built-in speakers?
The macos.rs implementation specifically excludes built-in speakers from output device lists while preserving Bluetooth peripherals. This design choice prioritizes common meeting workflows where users typically want to capture audio from headphones or external speakers rather than internal laptop speakers, reducing accidental misconfiguration.
What fallback behavior exists if WASAPI fails on Windows?
Per the source in windows.rs, the implementation falls back to the default cpal host if WASAPI enumeration fails. This ensures basic functionality even on systems with problematic audio drivers or restricted environments, though advanced features like loopback capture may be unavailable in fallback mode.
How are platform differences hidden from the frontend TypeScript code?
The frontend invokes a single Tauri command (list_audio_devices) with no platform parameters. The Rust backend resolves the appropriate implementation at compile time through #[cfg(target_os = ...)] attributes in mod.rs, then returns a uniform Vec<AudioDevice> regardless of the underlying OS-specific code path executed.
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 →