# How Meetily Handles Audio Device Enumeration Across macOS, Windows, and Linux

> Discover how Meetily enumerates audio devices across macOS, Windows, and Linux using a Rust Tauri core and OS-specific APIs. Learn about platform-aware audio handling and hardware filtering.

- Repository: [Zackriya Solutions/meetily](https://github.com/Zackriya-Solutions/meetily)
- Tags: internals
- Published: 2026-07-26

---

**Meetily uses a platform-aware Rust layer in its Tauri core that delegates to OS-specific audio APIs—WASAPI on Windows, CoreAudio on macOS, and PulseAudio/ALSA on Linux—to enumerate input and output devices while filtering out incompatible hardware like built-in speakers.**

Meetily is an open-source meeting assistant built with a **Tauri-Rust** backend and React frontend. Reliable audio capture requires robust **audio device enumeration** that adapts to each operating system's native sound architecture, ensuring the frontend presents a consistent device picker regardless of whether the app runs on macOS, Windows, or Linux.

## Architecture Overview

The central entry point for device discovery is the `list_audio_devices()` function in [`frontend/src-tauri/src/audio/devices/discovery.rs`](https://github.com/Zackriya-Solutions/meetily/blob/main/frontend/src-tauri/src/audio/devices/discovery.rs). This async method uses **compile-time conditional compilation** to dispatch to platform-specific implementations while maintaining a unified `Result<Vec<AudioDevice>>` return type.

```rust
pub async fn list_audio_devices() -> Result<Vec<AudioDevice>> {
    let host = cpal::default_host();

    let mut devices = {
        #[cfg(target_os = "windows")]
        { platform::configure_windows_audio(&host)? }

        #[cfg(target_os = "linux")]
        { platform::configure_linux_audio(&host)? }

        #[cfg(target_os = "macos")]
        { platform::configure_macos_audio(&host)? }
    };
    
    // Additional generic enumeration and de-duplication logic follows
}

```

After the platform-specific call returns, the function merges results from the generic `host.devices()` iterator, de-duplicates entries by device name, and returns a consolidated list of `AudioDevice` structs defined in [`frontend/src-tauri/src/audio/devices/configuration.rs`](https://github.com/Zackriya-Solutions/meetily/blob/main/frontend/src-tauri/src/audio/devices/configuration.rs).

## Platform-Specific Implementations

### Windows via WASAPI

On Windows, `configure_windows_audio` in [`frontend/src-tauri/src/audio/devices/platform/windows.rs`](https://github.com/Zackriya-Solutions/meetily/blob/main/frontend/src-tauri/src/audio/devices/platform/windows.rs) specifically targets the **WASAPI** host for enumeration. The implementation attempts to create a dedicated WASAPI host via `cpal::host_from_id(cpal::HostId::Wasapi)` rather than relying solely on the default host, providing lower-level access to input and output devices.

If WASAPI enumeration fails or returns no devices, the code falls back to the default CPAL host. Additionally, if the system reports no available devices, the function explicitly injects the system's default input and output devices to ensure the user always has selectable options.

### macOS via CoreAudio

macOS enumeration is handled by `configure_macos_audio` in [`frontend/src-tauri/src/audio/devices/platform/macos.rs`](https://github.com/Zackriya-Solutions/meetily/blob/main/frontend/src-tauri/src/audio/devices/platform/macos.rs). This module uses the default CPAL host, which interfaces with **CoreAudio** under the hood. While Meetily uses **ScreenCaptureKit** for actual system audio capture, the enumeration phase relies on CPAL's CoreAudio bindings for consistency.

A critical platform-specific filter removes built-in speakers from the output device list. Unlike external interfaces or virtual cables, built-in speakers cannot function as system audio capture sources, so excluding them prevents users from selecting invalid recording targets.

### Linux via PulseAudio and ALSA

Linux device discovery in [`frontend/src-tauri/src/audio/devices/platform/linux.rs`](https://github.com/Zackriya-Solutions/meetily/blob/main/frontend/src-tauri/src/audio/devices/platform/linux.rs) follows a pattern similar to macOS but accounts for the Linux audio stack's complexity. The `configure_linux_audio` function queries the default CPAL host, which automatically handles **PulseAudio** or **ALSA** backends depending on the system's configuration.

Like the Windows implementation, the Linux module includes fallback logic to ensure that at least the default input and output devices are returned even if comprehensive enumeration encounters errors, guaranteeing that the frontend always receives a non-empty device list.

## Permission Handling

Before recording, Meetily must obtain microphone permissions on macOS and Windows. The `trigger_audio_permission()` function, located in the same [`discovery.rs`](https://github.com/Zackriya-Solutions/meetily/blob/main/discovery.rs) module, addresses this by constructing a short-lived input stream. This temporary stream forces the operating system to display the native permission dialog if the user has not previously granted access, returning a boolean status that the frontend can use to guide the user.

## Frontend Integration

The Rust functions are exposed to the TypeScript frontend through Tauri commands. Here are the common integration patterns:

**Listing available devices:**

```typescript
import { invoke } from '@tauri-apps/api/tauri';

async function fetchAudioDevices() {
  const devices: { name: string; type: 'Input' | 'Output' }[] =
    await invoke('list_audio_devices');
  console.log('Audio devices:', devices);
  return devices;
}

```

**Requesting microphone permissions:**

```typescript
import { invoke } from '@tauri-apps/api/tauri';

async function requestPermission() {
  const granted: boolean = await invoke('trigger_audio_permission');
  if (!granted) {
    alert('Audio permission was denied – recording will not work.');
  }
}

```

**Starting a recording session:**

```typescript
import { invoke } from '@tauri-apps/api/tauri';

async function startRecording(micName: string, systemName: string) {
  await invoke('start_recording', {
    mic_device_name: micName,
    system_device_name: systemName,
    meeting_name: 'Team Sync',
  });
}

```

## Summary

- **Centralized dispatch**: `list_audio_devices()` in [`frontend/src-tauri/src/audio/devices/discovery.rs`](https://github.com/Zackriya-Solutions/meetily/blob/main/frontend/src-tauri/src/audio/devices/discovery.rs) coordinates platform-specific enumeration using conditional compilation.
- **Windows implementation**: Leverages **WASAPI** explicitly via [`frontend/src-tauri/src/audio/devices/platform/windows.rs`](https://github.com/Zackriya-Solutions/meetily/blob/main/frontend/src-tauri/src/audio/devices/platform/windows.rs), with fallback to default hosts and guaranteed default device injection.
- **macOS implementation**: Uses CoreAudio through CPAL in [`frontend/src-tauri/src/audio/devices/platform/macos.rs`](https://github.com/Zackriya-Solutions/meetily/blob/main/frontend/src-tauri/src/audio/devices/platform/macos.rs), filtering built-in speakers from output options.
- **Linux implementation**: Handles PulseAudio/ALSA via CPAL in [`frontend/src-tauri/src/audio/devices/platform/linux.rs`](https://github.com/Zackriya-Solutions/meetily/blob/main/frontend/src-tauri/src/audio/devices/platform/linux.rs), mirroring the fallback strategies of other platforms.
- **Permission management**: `trigger_audio_permission()` provokes OS dialogs by creating temporary audio streams on platforms requiring explicit consent.
- **Unified interface**: All platforms return standardized `AudioDevice` structs, allowing the React frontend to present a consistent selection interface without platform-specific logic.

## Frequently Asked Questions

### What audio APIs does Meetily use for device enumeration?

Meetily relies on the **CPAL** (Cross-Platform Audio Library) Rust crate as its abstraction layer. On Windows, it specifically targets **WASAPI** for more reliable device access; on macOS, it uses **CoreAudio**; and on Linux, it delegates to **PulseAudio** or **ALSA** depending on system configuration.

### How does Meetily handle platforms where no audio devices are detected?

According to the source code in the platform-specific modules, if enumeration returns an empty list—particularly on Windows and Linux—the code explicitly queries and injects the system's default input and output devices. This ensures the frontend always receives at least one selectable option per device type.

### Why does Meetily filter out built-in speakers on macOS?

Built-in speakers cannot capture system audio; they are output-only hardware. The macOS-specific configuration in [`frontend/src-tauri/src/audio/devices/platform/macos.rs`](https://github.com/Zackriya-Solutions/meetily/blob/main/frontend/src-tauri/src/audio/devices/platform/macos.rs) filters these out during enumeration to prevent users from inadvertently selecting them as recording sources, which would result in silence or errors during capture.

### How does the frontend request audio permissions before recording?

The frontend invokes the `trigger_audio_permission` Tauri command, which is implemented in [`frontend/src-tauri/src/audio/devices/discovery.rs`](https://github.com/Zackriya-Solutions/meetily/blob/main/frontend/src-tauri/src/audio/devices/discovery.rs). This Rust function creates a temporary input stream just long enough to trigger the operating system's native permission dialog, returning `true` if access is granted or `false` if denied.