# Milestone 2: Local Audio & GUI (The Ears) **Goal:** Enable the client to process high-quality audio locally and display the interface. ### 1. UI Layout (`client_node/ui`) - [ ] **Dependencies:** Add `egui`, `eframe`. - [ ] **AI Context Trap (Eframe + Tokio):** Do NOT use `#[tokio::main]` on the client. `eframe` demands the main thread. Manually build a `tokio::runtime::Runtime`, spawn the background network actors, and pass MPSC channels into the `AppState` before calling `eframe::run_native()`. - [ ] **Architecture:** Create `struct AppState`. Implement `eframe::App` trait for it. - [ ] **Layout:** Build the basic classic TeamSpeak UI. Left panel (tree view of hardcoded channels), right panel (text chat log). ### 2. Audio Capture (`client_node/audio/capture.rs`) - [ ] **Dependencies:** Add `cpal`, `ringbuf`. - [ ] **Device Setup:** Use `cpal::default_host().default_input_device()`. Build a stream config specifically requesting `48,000 Hz` and `1 channel` (Mono). - [ ] **Headless Abstraction:** Ensure the `cpal` instantiation is hidden behind a trait so the CI test suite can inject deterministic "sine wave" `f32` vectors instead of requiring a physical microphone. - [ ] **The Producer:** Create a `ringbuf::HeapRb` (e.g., 4096 capacity). Split it into `(producer, consumer)`. - [ ] **Hardware Callback:** Inside the `cpal` data callback, write the raw `f32` samples directly into the `producer`. Strictly `no_std`-like rules here (no allocations, no locks). ### 3. DSP Chain & VAD (`client_node/audio/dsp.rs`) - [ ] **Dependencies:** Add `webrtc-audio-processing`. - [ ] **Thread Spawning:** Spawn a standard `std::thread` (not tokio) to act as the Audio Consumer. - [ ] **Processing Loop:** Pull chunks of exactly `960` samples (20ms) from the `consumer` ringbuffer. - [ ] **Filters:** Pass the 960 samples through `webrtc`'s `EchoCancellation` and `NoiseSuppression` methods. - [ ] **Voice Activity Detection (VAD):** Implement `webrtc` VAD or an amplitude threshold calculator. If the chunk is "silence", drop it to save bandwidth. ### 4. Global Hotkeys / Push-To-Talk (PTT) - [ ] **Dependencies:** Add `global-hotkey` (or `rdev`). - [ ] **Event Loop:** Spawn a thread to listen for a specific keycode (e.g., `Mouse4` or `V`). - [ ] **Integration:** Update an `Arc` `is_transmitting` flag. The DSP thread reads this flag; if false, it dumps the audio chunks. ### 5. Local Loopback & UI Bridge - [ ] **Loopback Thread:** For testing, route the post-DSP 960-sample chunks directly into a `cpal` output stream (Speaker) to physically hear the microphone quality and VAD gating. - [ ] **UI State Bridge:** Use `tokio::sync::mpsc` or simple `Arc` to signal the UI thread when VAD triggers, so `egui` can draw the green "Active Speaker" dot next to the user's name. - [ ] **Audio Dumper UI:** Add a checkbox in the `egui` settings panel. When checked, write the 960-sample chunks to `raw_mic.wav` and `post_dsp.wav` using the `hound` crate for local inspection.