Platform-Specific Audio Device Discovery in Meetily: Windows, macOS, and Linux
Meetily abstracts audio hardware enumeration behind a modular audio::devices layer that leverages WASAPI on Windows, ScreenCaptureKit on macOS, and ALSA/PulseAudio on Linux to detect microphones and speakers.
The open-source Meetily project handles cross-platform audio capture through dedicated platform modules written in Rust. Each implementation resides in frontend/src-tauri/src/audio/devices/platform/ and uses the host operating system's native audio APIs to enumerate capture and playback devices, exposed via a unified interface that insulates the UI from OS-level complexity.
Platform-Specific Implementation Overview
Meetily organizes its audio device discovery logic into three platform-specific modules, each targeting the native API stack of its respective operating system:
| Platform | Implementation File | Native API |
|---|---|---|
| Windows | frontend/src-tauri/src/audio/devices/platform/windows.rs |
WASAPI (Windows Audio Session API) |
| macOS | frontend/src-tauri/src/audio/devices/platform/macos.rs |
ScreenCaptureKit and CoreAudio |
| Linux | frontend/src-tauri/src/audio/devices/platform/linux.rs |
ALSA and PulseAudio |
Each module implements a discover_devices() function that returns a vector of AudioDevice structs containing the device name, unique ID, and type (microphone or system audio).
Windows Audio Device Discovery via WASAPI
On Windows, Meetily uses the Windows Audio Session API (WASAPI) to enumerate audio endpoints. The implementation in frontend/src-tauri/src/audio/devices/platform/windows.rs handles device enumeration and supports exclusive-mode handling for low-latency capture.
The WASAPI implementation queries the IMMDeviceEnumerator interface to list active capture and render devices, converting the retrieved endpoint data into Meetily's internal AudioDevice representation.
macOS Audio Device Discovery via ScreenCaptureKit
For macOS, the macos.rs module utilizes ScreenCaptureKit for system audio discovery alongside CoreAudio for microphone enumeration. This dual-framework approach allows Meetily to capture both application audio streams and physical input devices.
ScreenCaptureKit provides modern APIs for capturing system audio without requiring kernel extensions, while CoreAudio handles traditional audio hardware enumeration for microphones and external audio interfaces.
Linux Audio Device Discovery via ALSA and PulseAudio
The Linux implementation in linux.rs probes both ALSA and PulseAudio backends to maximize compatibility across distributions. The module first attempts to enumerate devices via PulseAudio's asynchronous API, falling back to ALSA hardware enumeration when PulseAudio is unavailable.
This dual-backend strategy ensures Meetily functions on both modern desktop environments running PipeWire/PulseAudio and minimal systems using direct ALSA access.
Unified API Architecture and Conditional Compilation
The platform-specific modules are abstracted behind a common interface defined in frontend/src-tauri/src/audio/devices/mod.rs. This module uses Rust's conditional compilation attributes (#[cfg(target_os = "...")]) to re-export the appropriate discover_devices() function for the target platform.
The frontend/src-tauri/src/audio/devices/discovery.rs file provides the high-level list_audio_devices() API that the rest of the codebase consumes. When the UI requests available devices, the backend executes the following flow:
- The Tauri command invokes
audio::devices::list_audio_devices() - Conditional compilation routes the call to the platform-specific
discover_devices() - The native API queries hardware and returns a
Vec<AudioDevice> - The UI receives structured data to populate device selection dropdowns
use crate::audio::devices::list_audio_devices;
pub async fn show_device_picker() -> Result<(), anyhow::Error> {
// Retrieve all devices for the current platform
let devices = list_audio_devices()?;
// Filter for microphone and system audio devices
let microphones = devices.iter()
.filter(|d| d.kind == AudioDeviceKind::Microphone)
.collect::<Vec<_>>();
let systems = devices.iter()
.filter(|d| d.kind == AudioDeviceKind::System)
.collect::<Vec<_>>();
// Example: pick the first available device on each side
let mic = microphones.first()
.ok_or_else(|| anyhow!("No microphone found"))?
.clone();
let sys = systems.first()
.ok_or_else(|| anyhow!("No system audio device found"))?
.clone();
// Pass the selections to the recording manager
crate::audio::recording_manager::start(mic, sys).await?;
Ok(())
}
For direct platform testing, you can import the specific implementation using conditional compilation:
#[cfg(target_os = "windows")]
fn dump_windows_devices() -> Result<(), anyhow::Error> {
use crate::audio::devices::platform::windows::discover_devices;
let devs = discover_devices()?;
println!("Windows audio devices: {:#?}", devs);
Ok(())
}
Frontend Integration with TypeScript
The React frontend retrieves device lists by invoking the Tauri command, which returns typed data for populating UI selectors:
import { invoke } from '@tauri-apps/api/tauri';
// Ask the Rust backend for the list of devices
async function populateDeviceSelectors() {
const devices: AudioDevice[] = await invoke('list_audio_devices');
// `AudioDevice` contains `{ id: string, name: string, kind: 'microphone' | 'system' }`
setMicOptions(devices.filter(d => d.kind === 'microphone'));
setSystemOptions(devices.filter(d => d.kind === 'system'));
}
When the user initiates recording via start_recording, the selected device IDs are passed to frontend/src-tauri/src/audio/recording_manager.rs, which coordinates the audio pipeline and Whisper transcription services.
Summary
- Windows implementation uses WASAPI for device enumeration in
frontend/src-tauri/src/audio/devices/platform/windows.rs - macOS implementation combines ScreenCaptureKit and CoreAudio in
frontend/src-tauri/src/audio/devices/platform/macos.rs - Linux implementation supports both ALSA and PulseAudio in
frontend/src-tauri/src/audio/devices/platform/linux.rs - The
audio::devicesmodule abstracts platform differences through#[cfg]conditional compilation and the publiclist_audio_devices()API - Device discovery integrates with
recording_manager.rsto orchestrate audio capture and transcription workflows
Frequently Asked Questions
How does Meetily handle audio device discovery across different operating systems?
Meetily uses a platform abstraction layer where each OS has a dedicated implementation file that calls native APIs—WASAPI for Windows, ScreenCaptureKit/CoreAudio for macOS, and ALSA/PulseAudio for Linux. These are compiled conditionally using Rust's #[cfg(target_os = "...")] attributes, exposing a uniform list_audio_devices() function to the rest of the application.
What Rust pattern does Meetily use to select the correct platform implementation?
The project uses conditional compilation via the cfg attribute in frontend/src-tauri/src/audio/devices/mod.rs to re-export the appropriate discover_devices() function based on the target OS. At compile time, only the relevant platform module is included, ensuring efficient binaries without unused code.
Can Meetily's audio device discovery be extended to support additional platforms?
Yes, adding a new platform requires creating a new file under frontend/src-tauri/src/audio/devices/platform/, implementing the discover_devices() function using that OS's native audio API, and adding the corresponding #[cfg(target_os = "...")] export to mod.rs. This modular structure keeps platform-specific code isolated and maintainable.
How does the frontend retrieve and display available audio devices?
The TypeScript frontend calls invoke('list_audio_devices') from @tauri-apps/api/tauri, which triggers the Rust backend to execute the platform-specific discovery logic. The returned array of AudioDevice objects contains id, name, and kind fields, allowing the React UI to filter and populate dropdown menus for microphone and system audio selection.
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 →