How Meetily Handles Audio Device Connect/Disconnect Events: Device Detection and Monitoring Explained

Meetily's audio subsystem uses a dedicated async monitor to watch for OS-level device changes, updates a shared registry, and emits Tauri events to keep the React frontend synchronized when microphones or speakers are added or removed.

The device detection and monitoring system in Zackriya-Solutions/meetily provides real-time tracking of audio hardware across macOS, Windows, and Linux. Built with Rust and Tauri, the architecture separates enumeration logic from continuous monitoring to ensure low-latency detection of connect and disconnect events without blocking the main recording pipeline.

Core Architecture Components

The system relies on two tightly-coupled modules that handle distinct responsibilities in the audio stack.

Device Detection Module (device_detection.rs)

Located at frontend/src-tauri/src/audio/device_detection.rs, this module performs the initial enumeration of audio devices and provides helper functions to retrieve default microphones or speakers. It queries platform-specific backends found in audio/devices/platform/*.rs to build a list of AudioDevice structs, each describing the device name, ID, and capabilities.

Device Monitor Module (device_monitor.rs)

The frontend/src-tauri/src/audio/device_monitor.rs file spawns a background async task that registers OS-level listeners. On macOS it uses CoreAudio callbacks, on Windows it leverages WASAPI notifications, and on Linux it listens for PulseAudio or ALSA events. When the OS reports a device addition or removal, the monitor updates the shared device registry and emits a Tauri event to the frontend.

Event Handling Flow

The detection pipeline follows a four-stage lifecycle from startup enumeration through runtime monitoring.

Startup Enumeration

When the Tauri application launches, audio::mod.rs calls device_detection::list_audio_devices() to gather the current snapshot of available hardware. This initial list populates the frontend device selectors before monitoring begins.

OS-Level Monitoring

The device_monitor::start() function creates a Tokio-based watcher that registers with the platform module (audio/devices/platform/{macos,windows,linux}.rs). Each platform implementation provides a register_device_change_callback that forwards OS events to a mpsc::UnboundedSender<DeviceChange> channel. When the callback triggers, it translates native notifications into DeviceChange::Added or DeviceChange::Removed messages.

Frontend Synchronization

Upon receiving device change messages, the monitor updates an Arc<RwLock<Vec<AudioDevice>>> shared state and calls app.emit("device-updated", ...) to broadcast the new device list. The React UI listens for this event to refresh selectors in real time, ensuring users always see current hardware availability.

Stream Recovery and Graceful Degradation

The frontend/src-tauri/src/audio/recording_manager.rs coordinates the recording lifecycle and reacts to missing devices via the shared state. If an active recording loses its selected input device, the manager detects the absence through the registry and gracefully stops the capture stream, notifying the user through a toast notification. When new devices appear, the UI can automatically select them or preserve the previous selection if still available.

Because the monitor uses a lock-free Arc<AtomicBool> flag for the "recording-in-progress" state, device-change handling remains lightweight and does not interfere with the real-time audio mixing pipeline.

Implementation Examples

The following code demonstrates how to initialize the monitoring system and handle events in both the Rust backend and TypeScript frontend.

Initialize the monitor and emit initial device list:

#[tauri::command]
async fn init_audio(app: tauri::AppHandle) -> Result<(), String> {
    // Enumerate devices once
    let devices = audio::device_detection::list_audio_devices()?;
    app.emit("device-updated", devices).map_err(|e| e.to_string())?;

    // Begin background monitoring
    audio::device_monitor::start(app.clone());
    Ok(())
}

Platform-specific registration for macOS using CoreAudio:

#[cfg(target_os = "macos")]
pub fn register_device_change_callback(tx: UnboundedSender<DeviceChange>) {
    // Use CoreAudio AudioObjectAddPropertyListener to watch
    // AudioObjectPropertyAddress for device list changes
    // On callback, translate to DeviceChange::Added/Removed and send via tx
}

Frontend subscription to device updates:

import { listen } from '@tauri-apps/api/event';

listen('device-updated', (event) => {
  const devices = event.payload as AudioDevice[];
  setAvailableDevices(devices);
});

Summary

  • Separation of concerns: device_detection.rs handles static enumeration while device_monitor.rs manages dynamic OS events
  • Cross-platform abstraction: Platform-specific modules in audio/devices/platform/ normalize CoreAudio, WASAPI, and PulseAudio notifications into uniform DeviceChange events
  • Thread-safe state: The system uses Arc<RwLock<Vec<AudioDevice>>> for shared device registry and Arc<AtomicBool> for recording state to prevent race conditions
  • Tauri integration: Device changes propagate to the React frontend via the device-updated event, enabling real-time UI updates
  • Graceful degradation: recording_manager.rs detects missing devices during active recordings and stops streams safely rather than crashing

Frequently Asked Questions

How does Meetily detect audio device changes on macOS?

On macOS, the platform module in audio/devices/platform/macos.rs registers a CoreAudio property listener using AudioObjectAddPropertyListener on the default audio object. When the system broadcasts device list changes, the callback translates the native event into a DeviceChange enum variant and sends it through an unbounded channel to the async monitor task.

What happens to active recordings when a device is disconnected?

The recording_manager.rs module continuously checks the shared device registry against the currently selected recording input. If the selected device disappears from the registry, the manager triggers a graceful shutdown of the capture stream, releases the audio resources, and emits a notification event to the frontend to alert the user of the hardware change.

Can the frontend automatically switch to a newly connected microphone?

Yes. When device_monitor.rs detects a DeviceChange::Added event, it emits the full updated device list via the device-updated Tauri event. The React frontend can implement logic to automatically select the new device if no current selection exists, or prioritize it based on device properties, by handling this event in the component state management.

Which file handles the platform-specific Windows audio notifications?

Windows-specific device notifications are implemented in frontend/src-tauri/src/audio/devices/platform/windows.rs. This file registers for WASAPI endpoint change notifications using the IMMNotificationClient interface, converting Windows OnDeviceAdded and OnDeviceRemoved callbacks into the internal DeviceChange message types consumed by the monitor.

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 →