Meetily's Platform-Specific Audio Device Enumeration Methods Explained

Meetily enumerates audio devices using the cpal crate with platform-specific backends—WASAPI for Windows, CoreAudio for macOS, and ALSA/PulseAudio for Linux—through dedicated modules in frontend/src-tauri/src/audio/devices/platform/.

Meetily implements distinct audio device enumeration strategies for each supported operating system to ensure reliable capture of microphone and system audio. The open-source meeting assistant leverages Rust's cpal library alongside native APIs to discover available inputs and outputs. Understanding Meetily's platform-specific audio device enumeration methods reveals how the application handles cross-platform audio discovery while maintaining a unified device abstraction layer.

Windows Audio Enumeration via WASAPI

On Windows, Meetily relies on the Windows Audio Session API (WASAPI) to enumerate capture and playback devices. The implementation resides in frontend/src-tauri/src/audio/devices/platform/windows.rs within the configure_windows_audio function.

The method first attempts to create a WASAPI-specific host using cpal::host_from_id(cpal::HostId::Wasapi). It then enumerates output devices (including loopback capabilities) and input devices via output_devices() and input_devices(). If WASAPI initialization fails, the code falls back to the default host's enumeration methods. As a final safeguard, it adds the default input and output devices using host.default_input_device() and host.default_output_device().

// frontend/src-tauri/src/audio/devices/platform/windows.rs
pub fn configure_windows_audio(host: &cpal::Host) -> Result<Vec<AudioDevice>> {
    // Attempt WASAPI host first
    let wasapi_host = cpal::host_from_id(cpal::HostId::Wasapi)?;
    let mut devices = Vec::new();
    
    // Enumerate outputs and inputs
    for device in wasapi_host.output_devices()? {
        devices.push(AudioDevice::new(device.name()?, DeviceType::Output));
    }
    for device in wasapi_host.input_devices()? {
        devices.push(AudioDevice::new(device.name()?, DeviceType::Input));
    }
    
    // Fallback logic for default devices if enumeration fails
    Ok(devices)
}

macOS Audio Enumeration via CoreAudio

For macOS, Meetily utilizes the CoreAudio framework through cpal::host_from_id(cpal::HostId::CoreAudio). The platform-specific logic is implemented in frontend/src-tauri/src/audio/devices/platform/macos.rs inside the configure_macos_audio function.

Beyond standard microphone and speaker enumeration, this module handles the special case of system audio capture using ScreenCaptureKit. It exposes a virtual "system" device that feeds audio into the capture pipeline, enabling the recording of application audio without requiring physical loopback cables.

// frontend/src-tauri/src/audio/devices/platform/macos.rs
pub fn configure_macos_audio(host: &cpal::Host) -> Result<Vec<AudioDevice>> {
    let coreaudio_host = cpal::host_from_id(cpal::HostId::CoreAudio)?;
    let mut devices = Vec::new();
    
    // Standard device enumeration
    for device in coreaudio_host.input_devices()? {
        devices.push(AudioDevice::new(device.name()?, DeviceType::Input));
    }
    for device in coreaudio_host.output_devices()? {
        devices.push(AudioDevice::new(device.name()?, DeviceType::Output));
    }
    
    // Virtual system audio device for ScreenCaptureKit integration
    devices.push(AudioDevice::new("System Audio".to_string(), DeviceType::Output));
    Ok(devices)
}

Linux Audio Enumeration via ALSA and PulseAudio

Linux support is implemented in frontend/src-tauri/src/audio/devices/platform/linux.rs through the configure_linux_audio function. Meetily attempts to use either the ALSA or PulseAudio host backends depending on availability.

The code selects the preferred host using cpal::host_from_id(cpal::HostId::Alsa) or cpal::HostId::PulseAudio, then enumerates devices using input_devices() and output_devices(). If the preferred host is unavailable, it falls back to the default host configuration to ensure compatibility across different Linux distributions.

// frontend/src-tauri/src/audio/devices/platform/linux.rs
pub fn configure_linux_audio(host: &cpal::Host) -> Result<Vec<AudioDevice>> {
    // Try ALSA first, then PulseAudio
    let linux_host = cpal::host_from_id(cpal::HostId::Alsa)
        .or_else(|_| cpal::host_from_id(cpal::HostId::PulseAudio))?;
    
    let mut devices = Vec::new();
    for device in linux_host.input_devices()? {
        devices.push(AudioDevice::new(device.name()?, DeviceType::Input));
    }
    for device in linux_host.output_devices()? {
        devices.push(AudioDevice::new(device.name()?, DeviceType::Output));
    }
    Ok(devices)
}

The Discovery Orchestration Layer

The generic discovery flow in frontend/src-tauri/src/audio/devices/discovery.rs orchestrates platform-specific enumeration using compile-time configuration checks. It selects the appropriate module based on the target OS via cfg!(target_os = "...") macros and aggregates results into a Vec<AudioDevice>.

Each device is constructed using AudioDevice::new(name, DeviceType::Input) or DeviceType::Output, defined in frontend/src-tauri/src/audio/devices/configuration.rs. This abstraction ensures the frontend receives a consistent device structure regardless of the underlying platform.

// frontend/src-tauri/src/audio/devices/discovery.rs
pub async fn list_audio_devices() -> Result<Vec<AudioDevice>> {
    let host = cpal::default_host();
    
    #[cfg(target_os = "windows")]
    let devices = platform::windows::configure_windows_audio(&host)?;
    
    #[cfg(target_os = "macos")]
    let devices = platform::macos::configure_macos_audio(&host)?;
    
    #[cfg(target_os = "linux")]
    let devices = platform::linux::configure_linux_audio(&host)?;
    
    Ok(devices)
}

Frontend Integration with Tauri Commands

The device list is exposed to the frontend through Tauri commands defined in frontend/src-tauri/src/audio/recording_commands.rs. The TypeScript frontend invokes these commands using the Tauri API to retrieve the enumerated devices for display in the user interface.

// Frontend TypeScript integration
import { invoke } from '@tauri-apps/api/tauri';

async function getAudioDevices() {
  const devices = await invoke<AudioDevice[]>('list_audio_devices');
  console.log('Available audio devices:', devices);
  return devices;
}

Summary

Frequently Asked Questions

How does Meetily handle audio device enumeration on Windows?

Meetily attempts to create a WASAPI host first, then enumerates output and input devices including loopback capabilities. If WASAPI fails, it falls back to the default host's enumeration methods, and finally adds the default input and output devices as a last resort. This logic is contained in the configure_windows_audio function in frontend/src-tauri/src/audio/devices/platform/windows.rs.

What makes macOS audio enumeration different in Meetily?

The macOS implementation uses CoreAudio as its backend but includes specialized handling for system audio capture through ScreenCaptureKit. It exposes a virtual "system" device that allows recording of application audio without physical hardware, implemented in frontend/src-tauri/src/audio/devices/platform/macos.rs.

Which Linux audio backends does Meetily support?

Meetily supports both ALSA and PulseAudio backends. The code attempts to initialize the ALSA host first, then falls back to PulseAudio if unavailable. Both implementations use standard cpal enumeration methods and are located in frontend/src-tauri/src/audio/devices/platform/linux.rs.

How does the frontend request the audio device list?

The frontend calls the list_audio_devices command exposed through Tauri's command system in frontend/src-tauri/src/audio/recording_commands.rs. This async function invokes the platform-specific discovery logic and returns a typed array of AudioDevice objects that the TypeScript frontend can consume to populate device selection dropdowns.

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 →