diff --git a/src-tauri/src/commands/audio.rs b/src-tauri/src/commands/audio.rs index 1dd325b..63c9a7f 100644 --- a/src-tauri/src/commands/audio.rs +++ b/src-tauri/src/commands/audio.rs @@ -21,7 +21,10 @@ use ringbuf::{ }; use tauri::State; -use crate::{commands::config::ConfigState, crypto::SessionCipher}; +use crate::{ + commands::config::{BackendConfig, ConfigState}, + crypto::SessionCipher, +}; const TARGET_SAMPLE_RATE: u32 = 48_000; const PACKET_SAMPLES: usize = 960; @@ -57,6 +60,8 @@ pub struct VoiceSession { pub producer_in: AudioProducer, pub consumer_out: AudioConsumer, + + pub backend_config: Arc>, } fn resolve_input_device(device_name: &Option) -> Result { @@ -72,9 +77,9 @@ fn resolve_input_device(device_name: &Option) -> Result host.default_input_device().ok_or_else(|| { - format!("No default input device") - }), + None => host + .default_input_device() + .ok_or_else(|| format!("No default input device")), } } @@ -91,9 +96,9 @@ fn resolve_output_device(device_name: &Option) -> Result host.default_output_device().ok_or_else(|| { - format!("No default output device") - }), + None => host + .default_output_device() + .ok_or_else(|| format!("No default output device")), } } @@ -561,6 +566,11 @@ pub fn connect_to_vc( current_output_device: config.output_device_name.clone(), producer_in: Arc::clone(&shared_producer_in), consumer_out: Arc::clone(&shared_consumer_out), + + backend_config: Arc::new(Mutex::new(BackendConfig { + is_muted: false, + is_deaf: false, + })), }; session.setup_input(config.input_device_name.clone(), Arc::clone(&state_inner))?; @@ -571,6 +581,7 @@ pub fn connect_to_vc( let input_socket = socket.clone(); let shutdown = shutdown.clone(); let cipher = cipher.clone(); + let backend_config = session.backend_config.clone(); thread::spawn(move || { let mut sequence = 0u32; @@ -581,6 +592,22 @@ pub fn connect_to_vc( let mut noise_floor: f32 = 0.0; while !shutdown.load(Ordering::Relaxed) { + let (is_muted, is_deaf) = { + let cfg = backend_config.lock().unwrap(); + (cfg.is_muted, cfg.is_deaf) + }; + + if is_muted || is_deaf { + // Clear audio recorded while muted to avoid stale transmission on unmute + let available = consumer_in.occupied_len(); + if available > 0 { + consumer_in.skip(available); + } + + thread::sleep(Duration::from_millis(50)); + continue; + } + if consumer_in.occupied_len() >= PACKET_SAMPLES { let _ = consumer_in.pop_slice(&mut frame_buf); @@ -654,6 +681,7 @@ pub fn connect_to_vc( let socket = socket.clone(); let shutdown = shutdown.clone(); let cipher = cipher.clone(); + let backend_config = session.backend_config.clone(); thread::spawn(move || { let mut packets: BTreeMap> = BTreeMap::new(); @@ -666,6 +694,16 @@ pub fn connect_to_vc( let mut loop_count: u64 = 0; while !shutdown.load(Ordering::Relaxed) { + let is_deaf = { backend_config.lock().unwrap().is_deaf }; + + if is_deaf { + packets.clear(); + is_prebuffering = true; + + std::thread::sleep(Duration::from_secs(1)); + continue; + } + loop_count += 1; if let Ok(len) = socket.recv(&mut udp_buffer) { recv_count += 1; diff --git a/src-tauri/src/commands/config.rs b/src-tauri/src/commands/config.rs index 32af22b..c8d672f 100644 --- a/src-tauri/src/commands/config.rs +++ b/src-tauri/src/commands/config.rs @@ -102,3 +102,22 @@ pub fn get_config(app: AppHandle, state: State) -> Result, + config: BackendConfig, +) -> Result<(), String> { + if let Some(v) = &mut *voice_state.inner.session.lock().unwrap() { + *v.backend_config.lock().unwrap() = config + } + + Ok(()) +} diff --git a/src-tauri/src/lib.rs b/src-tauri/src/lib.rs index 73dbfc8..69fb5d8 100644 --- a/src-tauri/src/lib.rs +++ b/src-tauri/src/lib.rs @@ -24,6 +24,7 @@ pub fn run() { commands::config::update_config, commands::config::save_config, commands::config::get_config, + commands::config::update_backend_config, ]) .run(tauri::generate_context!()) .expect("error while running tauri application"); diff --git a/src/app/app.ts b/src/app/app.ts index 7fb3f50..8ca0cdb 100644 --- a/src/app/app.ts +++ b/src/app/app.ts @@ -10,7 +10,7 @@ import { Page } from "@/components/page/PageView"; import { Channel } from "@/lib/types"; import { invoke } from "@tauri-apps/api/core"; import { ClientMethod } from "./protocol"; -import { Config, getConfig } from "@/lib/config"; +import { BackendConfig, Config, getConfig } from "@/lib/config"; ed.hashes.sha512 = sha512; @@ -35,11 +35,16 @@ export default class Enclave

{ public forceRender: () => void; public page?: P; public config?: Config; + public backendConfig: BackendConfig; public constructor() { this.serverList = []; this.forceRender = () => {}; this.isSettingsOpen = false; + this.backendConfig = { + isMuted: false, + isDeaf: false, + }; } public async init() { @@ -113,12 +118,12 @@ export default class Enclave

{ return; } - const serveridx = this.serverList.findIndex((server) => { - server.hostname === hostname; - }); + const serveridx = this.serverList.findIndex( + (server) => server.hostname === hostname, + ); if ( - this.serverList[serveridx] && + serveridx >= 0 && this.serverList[serveridx].publicKey !== base58.encode(this.server.serverPublicKey) ) { @@ -167,7 +172,7 @@ export default class Enclave

{ meta: metaResponse.data as any, }; - if (this.serverList[serveridx]) { + if (serveridx >= 0) { this.serverList[serveridx] = serverItem; } else { this.serverList.push(serverItem); diff --git a/src/components/view/StatusCard.tsx b/src/components/view/StatusCard.tsx index 4644de8..b68016f 100644 --- a/src/components/view/StatusCard.tsx +++ b/src/components/view/StatusCard.tsx @@ -1,11 +1,20 @@ import { Card } from "../ui/card"; import { Avatar, AvatarFallback, AvatarImage } from "../ui/avatar"; -import { CopyIcon, PhoneOff, Settings2Icon, Signal } from "lucide-react"; +import { + CopyIcon, + Headset, + Mic, + MicOff, + PhoneOff, + Settings2Icon, + Signal, +} from "lucide-react"; import { Button } from "../ui/button"; import Enclave from "@/app/app"; import { base58 } from "@scure/base"; import * as ed from "@noble/ed25519"; import { Channel } from "@/lib/types"; +import { updateBackendConfig } from "@/lib/config"; function findChannel(channels: Channel[], id: string): Channel | undefined { for (const channel of channels) { @@ -38,9 +47,9 @@ export default function StatusCard({ return (

- + {voiceChannel && ( -
+
Voice Connected @@ -82,6 +91,46 @@ export default function StatusCard({
+ +