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.rshandles static enumeration whiledevice_monitor.rsmanages dynamic OS events - Cross-platform abstraction: Platform-specific modules in
audio/devices/platform/normalize CoreAudio, WASAPI, and PulseAudio notifications into uniformDeviceChangeevents - Thread-safe state: The system uses
Arc<RwLock<Vec<AudioDevice>>>for shared device registry andArc<AtomicBool>for recording state to prevent race conditions - Tauri integration: Device changes propagate to the React frontend via the
device-updatedevent, enabling real-time UI updates - Graceful degradation:
recording_manager.rsdetects 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:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →