# How Meetily Provides Real-Time Device Level Feedback to the UI

> Discover how Meetily delivers real-time device level feedback to your UI. Learn about its audio level monitoring and Rust processing for seamless updates to your React frontend.

- Repository: [Zackriya Solutions/meetily](https://github.com/Zackriya-Solutions/meetily)
- Tags: how-to-guide
- Published: 2026-07-06

---

**Meetily captures raw audio samples from input devices using CPAL, computes RMS and peak levels in Rust, and transmits updates to the React frontend every 100 milliseconds via Tauri's `audio-levels` event bus.**

The open-source meeting assistant from Zackriya-Solutions implements real-time device level monitoring to help users verify microphone input before joining calls. By bridging Rust's low-level audio processing capabilities with a TypeScript React frontend, the system delivers sub-100ms latency for visual feedback while maintaining thread-safe state management across the native boundary.

## The Audio Level Monitoring Pipeline

The architecture follows a six-stage pipeline that moves from device selection to UI rendering. First, the frontend invokes the `start_audio_level_monitoring` command defined in [`frontend/src-tauri/src/lib.rs`](https://github.com/Zackriya-Solutions/meetily/blob/main/frontend/src-tauri/src/lib.rs), passing a list of target device names. This command initializes the monitor in [`audio/level_monitor.rs`](https://github.com/Zackriya-Solutions/meetily/blob/main/audio/level_monitor.rs) or falls back to [`audio/simple_level_monitor.rs`](https://github.com/Zackriya-Solutions/meetily/blob/main/audio/simple_level_monitor.rs) during development.

Next, the monitor creates CPAL input streams for each device. In the stream callbacks, raw multi-channel samples pass through `audio_to_mono` (defined in [`frontend/src-tauri/src/audio/audio_processing.rs`](https://github.com/Zackriya-Solutions/meetily/blob/main/frontend/src-tauri/src/audio/audio_processing.rs)) before `process_audio_levels` calculates RMS and peak values. These results populate a shared `Arc<Mutex<Vec<AudioLevelData>>>` that aggregates data from all active streams.

Every 100 milliseconds, a background task locks the mutex, drains the vector, and packages the data into an `AudioLevelUpdate` struct containing a Unix-epoch timestamp and the device-level array. The task emits this payload via `app_handle.emit("audio-levels", &update)`, which traverses Tauri's event bus to the frontend.

On the UI side, the [`DeviceSelection.tsx`](https://github.com/Zackriya-Solutions/meetily/blob/main/DeviceSelection.tsx) component registers a listener using `listen<AudioLevelUpdate>('audio-levels', ...)`. Each incoming event updates a local `Map<string, AudioLevelData>` keyed by device name, triggering React re-renders that display live meters or activity indicators based on the `is_active` and RMS values.

## Backend Implementation: Capturing and Processing Audio

The core monitoring logic resides in [`frontend/src-tauri/src/audio/level_monitor.rs`](https://github.com/Zackriya-Solutions/meetily/blob/main/frontend/src-tauri/src/audio/level_monitor.rs). This module leverages the **CPAL** (Cross-Platform Audio Library) crate to enumerate and open input devices. For each requested device name, the monitor constructs an input stream with a callback that receives interleaved audio samples.

```rust
// Conceptual flow from level_monitor.rs
let stream = device.build_input_stream(
    &config,
    move |data: &[f32], _: &_| {
        let mono = audio_to_mono(data, channels);
        let levels = process_audio_levels(&mono);
        // Push to shared state...
    },
    err_fn,
    None,
)?;

```

The `audio_to_mono` function averages multi-channel samples into a single mono stream, ensuring consistent level calculations regardless of input configuration. Then `process_audio_levels` computes the root-mean-square (RMS) and peak amplitude. The system sets `is_active` to `true` when RMS exceeds `0.001`, filtering background noise while maintaining responsiveness.

For development scenarios where hardware access is unavailable, [`frontend/src-tauri/src/audio/simple_level_monitor.rs`](https://github.com/Zackriya-Solutions/meetily/blob/main/frontend/src-tauri/src/audio/simple_level_monitor.rs) provides a mock implementation. It generates synthetic level data on the same 100ms cadence and emits identical `audio-levels` events, allowing UI development without physical audio devices.

## Emitting Real-Time Events from Rust

Thread safety is critical when audio callbacks run on CPAL's threads while the main Tauri thread handles commands. The monitor stores device names and CPAL streams within `Arc<Mutex<...>>` structures, enabling safe concurrent access. An `AtomicBool` named `AUDIO_LEVEL_STATE.is_monitoring` provides lock-free start/stop control that prevents race conditions during rapid toggle operations.

The background emission task runs on an async interval:

```rust
// Emission logic from level_monitor.rs
let update = AudioLevelUpdate {
    timestamp: SystemTime::now()
        .duration_since(UNIX_EPOCH)
        .unwrap()
        .as_millis() as u64,
    levels: current_levels,
};

if let Err(e) = app_handle.emit("audio-levels", &update) {
    error!("Failed to emit audio levels: {}", e);
}

```

This pattern ensures that the frontend receives a complete snapshot of all device levels every 100 milliseconds, even if individual streams process samples at different rates. The `AudioLevelUpdate` struct acts as a serialization boundary, converting Rust vectors into JSON payloads that Tauri automatically deserializes for JavaScript consumption.

## Consuming Levels in the React Frontend

The [`DeviceSelection.tsx`](https://github.com/Zackriya-Solutions/meetily/blob/main/DeviceSelection.tsx) component manages the subscription lifecycle using React hooks. It imports the `listen` function from `@tauri-apps/api/event` and registers the handler inside a `useEffect` block:

```typescript
import { listen } from '@tauri-apps/api/event';
import type { AudioLevelUpdate, AudioLevelData } from '@/types';

const [audioLevels, setAudioLevels] = useState<Map<string, AudioLevelData>>(new Map());

useEffect(() => {
  let unlisten: (() => void) | undefined;

  const setup = async () => {
    unlisten = await listen<AudioLevelUpdate>('audio-levels', event => {
      const map = new Map<string, AudioLevelData>();
      event.payload.levels.forEach(l => map.set(l.device_name, l));
      setAudioLevels(map);
    });
  };
  setup();

  return () => unlisten?.();
}, []);

```

This approach maintains a `Map` that preserves device identity without requiring array searches on every update. When rendering, the component reads from `audioLevels` to set meter heights, change colors when `is_active` is true, or display numeric RMS values. The cleanup function removes the listener when the component unmounts, preventing memory leaks in the Tauri event bus.

## Thread Safety and Performance Optimizations

Several implementation details ensure the system remains responsive under load:

- **Atomic monitoring flag**: The `is_monitoring` atomic boolean in `AUDIO_LEVEL_STATE` allows instantaneous start/stop checks without blocking audio threads.
- **Mutex isolation**: The `Arc<Mutex<Vec<AudioLevelData>>>` separates high-frequency audio callbacks from the slower 100ms emission cycle, preventing buffer overflow during processing spikes.
- **Mono conversion**: By flattening multi-channel audio before analysis, the system guarantees consistent RMS values across stereo microphones and mono headsets alike.
- **Threshold filtering**: The `0.001` RMS threshold eliminates visual noise from silent channels while preserving sensitivity to actual speech.

## Summary

- Meetily uses CPAL in [`frontend/src-tauri/src/audio/level_monitor.rs`](https://github.com/Zackriya-Solutions/meetily/blob/main/frontend/src-tauri/src/audio/level_monitor.rs) to capture raw audio from selected input devices.
- The `audio_to_mono` and `process_audio_levels` functions calculate RMS and peak values, storing results in a thread-safe `Arc<Mutex<Vec<...>>>` structure.
- A background task emits `AudioLevelUpdate` payloads via the `audio-levels` Tauri event every 100 milliseconds.
- The React frontend in [`DeviceSelection.tsx`](https://github.com/Zackriya-Solutions/meetily/blob/main/DeviceSelection.tsx) listens for these events and maintains a `Map` of device levels to render real-time visual feedback.
- An `AtomicBool` flag and careful mutex usage prevent race conditions during rapid start/stop operations.

## Frequently Asked Questions

### How does Meetily calculate audio levels from raw samples?

Meetily processes raw audio samples through the `process_audio_levels` function in [`frontend/src-tauri/src/audio/level_monitor.rs`](https://github.com/Zackriya-Solutions/meetily/blob/main/frontend/src-tauri/src/audio/level_monitor.rs). First, `audio_to_mono` converts multi-channel interleaved data into a mono stream by averaging channels. Then the function calculates the root-mean-square (RMS) amplitude and tracks the peak absolute value over the sample buffer. An `is_active` boolean is set to `true` when RMS exceeds `0.001`, indicating detectable audio input above background noise.

### What is the difference between the full monitor and simple level monitor?

The full implementation in [`frontend/src-tauri/src/audio/level_monitor.rs`](https://github.com/Zackriya-Solutions/meetily/blob/main/frontend/src-tauri/src/audio/level_monitor.rs) opens actual CPAL streams to hardware devices and processes real audio samples. The simplified version in [`frontend/src-tauri/src/audio/simple_level_monitor.rs`](https://github.com/Zackriya-Solutions/meetily/blob/main/frontend/src-tauri/src/audio/simple_level_monitor.rs) generates synthetic level data programmatically on the same 100ms schedule. Both emit identical `audio-levels` events, allowing frontend development and testing without physical microphones or audio interfaces.

### How frequently does the UI receive audio level updates?

The UI receives updates every **100 milliseconds**. This cadence is controlled by a background async task in the Rust monitor that reads the shared state buffer, clears it, and emits the `audio-levels` event. This interval balances responsiveness with CPU usage, providing smooth visual feedback without overwhelming the JavaScript event loop.

### Why does Meetily convert multi-channel audio to mono before processing?

Multi-channel audio (stereo, quad, or surround) contains interleaved samples that would produce inconsistent level readings if analyzed directly. The `audio_to_mono` function in [`frontend/src-tauri/src/audio/audio_processing.rs`](https://github.com/Zackriya-Solutions/meetily/blob/main/frontend/src-tauri/src/audio/audio_processing.rs) averages all channels into a single mono stream, ensuring that RMS and peak calculations remain consistent regardless of whether the user connects a mono headset or stereo microphone. This standardization prevents volume meters from appearing artificially low when only one channel carries signal.