1pub mod device;
21pub mod sfx;
22
23use std::
24{
25 thread,
26 net::{ UdpSocket, TcpStream },
27 collections::{ HashMap, VecDeque },
28 time::
29 {
30 SystemTime,
31 Duration,
32 UNIX_EPOCH,
33 },
34 sync::
35 {
36 Arc,
37 Mutex,
38 LazyLock,
39 mpsc::Sender,
40 atomic::{ AtomicUsize, Ordering },
41 },
42};
43
44use cpal::
45{
46 Host,
47 Stream,
48 Device,
49 StreamConfig,
50 SupportedStreamConfig,
51 SupportedStreamConfigRange,
52 traits::
53 {
54 DeviceTrait,
55 HostTrait,
56 StreamTrait,
57 },
58};
59
60use audiopus::
61{
62 Channels,
63 SampleRate,
64 Application,
65 TryFrom,
66 coder::{ Encoder, Decoder },
67};
68
69use ringbuf::
70{
71 HeapRb,
72 HeapCons,
73 HeapProd,
74 traits::
75 {
76 Split,
77 Producer,
78 Consumer,
79 },
80};
81
82use nnnoiseless::DenoiseState;
83
84use gag::Gag;
85
86use crate::
87{
88 config,
89 command::{ self, Command },
90 options as chat_options,
91 network::
92 {
93 client::{ ClientEvent, VoiceUser },
94 voice::
95 {
96 consts,
97 self,
98 options,
99 VoiceCode,
100 VoicePacket,
101 client::sfx::SoundEffect,
102 },
103 },
104};
105
106struct LocalStream
108{
109 _input: Stream,
110 _output: Stream,
111}
112
113struct StreamGuard
114{
115 generation: usize,
116}
117
118struct RemoteStream
119{
120 consumer: HeapCons<f32>, resample_pos: f32, current_sample: f32, next_sample: f32, activity_hold: usize, display_hold: usize, username: String, latencies: VecDeque<u128>, avg_latency: u128, }
130
131pub struct PeerData
132{
133 decoder: Decoder, producer: HeapProd<f32>, }
136
137static LOCAL_STREAMS: LazyLock<Mutex<Option<LocalStream>>> = LazyLock::new(|| Mutex::new(None));
139static CONSUMERS: LazyLock<Mutex<HashMap<usize, (RemoteStream, PeerData)>>> = LazyLock::new(|| Mutex::new(HashMap::new())); static LOCAL_DISPLAY_HOLD: AtomicUsize = AtomicUsize::new(0);
142static AUDIO_GENERATION: AtomicUsize = AtomicUsize::new(0);
143
144impl Drop for StreamGuard
146{
147 fn drop(&mut self)
149 {
150 if AUDIO_GENERATION.load(Ordering::Relaxed) == self.generation
151 {
152 if let Ok(mut streams) = LOCAL_STREAMS.lock()
153 {
154 *streams = None;
155 }
156 }
157 }
158}
159
160fn find_devices(host: &Host) -> Option<(Device, Device)> {
163 let input_device = config::read_config::<String>("input_device");
165 let output_device = config::read_config::<String>("output_device");
166
167 let input_device = if !input_device.is_empty()
169 {
170 host.input_devices().ok()?.find(|d| d.description().is_ok_and(|desc| desc.to_string() == input_device))
171 } else { host.default_input_device() };
172
173 let output_device = if !output_device.is_empty()
175 {
176 host.output_devices().ok()?.find(|d| d.description().is_ok_and(|desc| desc.to_string() == output_device))
177 } else { host.default_output_device() };
178
179 Some((input_device?, output_device?))
180}
181
182fn configure_device(supported_configs: impl Iterator<Item = SupportedStreamConfigRange>, default_config: SupportedStreamConfig) -> StreamConfig
183{
184 supported_configs
185 .filter(|c| c.min_sample_rate() <= consts::SAMPLE_RATE && c.max_sample_rate() >= consts::SAMPLE_RATE)
186 .next()
187 .map(|c| c.with_sample_rate(consts::SAMPLE_RATE))
188 .unwrap_or(default_config)
189 .into()
190}
191
192fn transmit_audio(encoder: &Encoder, frame: &[f32], buffer: &mut [u8], id: usize, socket: &UdpSocket)
193{
194 if let Ok(len) = encoder.encode_float(&frame, buffer)
196 {
197 voice::send(socket, VoicePacket
199 {
200 voice: Some(buffer[..len].to_vec()),
201 id: Some(id),
202
203 ..Default::default()
204 }, &chat_options::get_keys().unwrap()).unwrap();
205 }
206}
207
208pub fn listen_server_voice (
211 id: usize,
212 username: String,
213 tx: Sender<ClientEvent>,
214 write_stream: Arc<Mutex<TcpStream>>
215)
216{
217 options::set_seq(0);
219 options::set_server_seq(0);
220
221 let current_generation = AUDIO_GENERATION.fetch_add(1, Ordering::Relaxed) + 1;
223 let _guard = StreamGuard { generation: current_generation };
224
225 let socket = Arc::new(UdpSocket::bind("0.0.0.0:0").expect("Binding UDP failed"));
227 socket.connect(chat_options::get_server_address()).expect("Connecting to server UDP failed");
228
229 socket.set_read_timeout(Some(Duration::from_millis(200))).expect("Setting socket timeout failed");
231
232 let host = cpal::default_host();
234
235 let stderr_gag = Gag::stderr().unwrap();
237
238 let (input_device, output_device) = match find_devices(&host)
240 {
241 Some((input, output)) => (input, output), _ => {
244 command::send_command_code(&mut write_stream.lock().unwrap(), &Command::Voice, &None);
246 return;
247 }
248 };
249
250 drop(stderr_gag);
252
253 voice::send(&socket, VoicePacket
255 {
256 id: Some(id),
257 ..Default::default()
258 }, &chat_options::get_keys().unwrap()).unwrap();
259
260 let input_config = configure_device(input_device.supported_input_configs().unwrap(), input_device.default_input_config().unwrap());
262
263 let output_config = configure_device(output_device.supported_output_configs().unwrap(), output_device.default_output_config().unwrap());
265
266 let opus_encoder = Encoder::new
268 (
269 <SampleRate as TryFrom<i32>>::try_from(consts::SAMPLE_RATE as i32).unwrap(),
270 Channels::Mono,
271 Application::Voip
272 ).unwrap();
273
274 let mut input_accum: Vec<f32> = Vec::with_capacity(consts::FRAME_SIZE * 2);
276 let mut encoded_buffer = [0u8; 1500]; let input_channels = input_config.channels as usize;
280 let input_source_rate = input_config.sample_rate as f32;
281 let input_target_rate = consts::SAMPLE_RATE as f32;
282
283 let input_resample_step = input_source_rate / input_target_rate;
285 let mut input_resample_pos = 0.;
286
287 let gate_open = Arc::new(Mutex::new(false)); let preroll_buffer = Arc::new(Mutex::new(VecDeque::<Vec<f32>>::with_capacity(3))); let hold_frames_remaining = Arc::new(Mutex::new(0usize)); let mut denoiser = DenoiseState::new();
294 let mut denoise_buffer = [0.0f32; consts::SAMPLE_RATE as usize / 100];
295
296 let send_socket = socket.clone();
298 let input_stream = input_device.build_input_stream(&input_config, move |data: &[f32], _: &_|
299 {
300 if chat_options::is_muted(None)
302 {
303 LOCAL_DISPLAY_HOLD.store(0, Ordering::Relaxed); input_accum.clear(); if let Ok(mut gate) = gate_open.lock()
308 {
309 *gate = false;
310 }
311
312 if let Ok(mut preroll) = preroll_buffer.lock()
314 {
315 preroll.clear();
316 }
317
318 return;
319 }
320
321 if AUDIO_GENERATION.load(Ordering::Relaxed) != current_generation { return; }
323
324 let frames_in_buffer = data.len() / input_channels;
325
326 let current_hold = LOCAL_DISPLAY_HOLD.load(Ordering::Relaxed);
327 if current_hold > 0
328 {
329 LOCAL_DISPLAY_HOLD.store(current_hold.saturating_sub(frames_in_buffer), Ordering::Relaxed);
330 }
331
332 let get_mono_sample = |index: usize| -> f32
334 {
335 if index >= frames_in_buffer { return 0. }
336
337 let mut sum = 0.;
338 for c in 0..input_channels
339 {
340 sum += data[index * input_channels + c];
341 }
342
343 sum / input_channels as f32
344 };
345
346 while input_resample_pos < (frames_in_buffer as f32) - 1.
348 {
349 let idx = input_resample_pos.floor() as usize;
350 let frac = input_resample_pos - idx as f32;
351
352 let s0 = get_mono_sample(idx);
353 let s1 = get_mono_sample(idx + 1);
354
355 let interpolated = s0 + (s1 - s0) * frac;
356 input_accum.push(interpolated);
357
358 input_resample_pos += input_resample_step;
359 }
360
361 input_resample_pos -= frames_in_buffer as f32;
363
364 while input_accum.len() >= consts::FRAME_SIZE
366 {
367 let mut frame: Vec<f32> = input_accum.drain(0..consts::FRAME_SIZE).collect();
368
369 for chunk in frame.chunks_mut(consts::SAMPLE_RATE as usize / 100)
371 {
372 if chunk.len() == consts::SAMPLE_RATE as usize / 100
373 {
374 for sample in chunk.iter_mut()
376 {
377 *sample *= 32767.;
378 }
379
380 denoiser.process_frame(&mut denoise_buffer, chunk);
382
383 for (i, sample) in denoise_buffer.iter().enumerate()
385 {
386 chunk[i] = sample / 32767.;
387 }
388 }
389 }
390
391 let rms = (frame.iter().map(|&x| x * x).sum::<f32>() / frame.len() as f32 + 1e-10).sqrt(); let mut gate = gate_open.lock().unwrap();
394 let mut preroll = preroll_buffer.lock().unwrap();
395 let mut hold_frames = hold_frames_remaining.lock().unwrap();
396
397 if !*gate && rms > consts::TRESHOLD_OPEN
399 {
400 *gate = true; for old_frame in preroll.iter()
404 {
405 transmit_audio(&opus_encoder, old_frame, &mut encoded_buffer, id, &send_socket);
406 }
407
408 preroll.clear();
409 *hold_frames = consts::HOLD_FRAMES;
410 } else if *gate && rms < consts::TRESHOLD_CLOSE
411 {
412 if *hold_frames > 0 {
414 *hold_frames -= 1;
415 } else {
417 *gate = false;
418 }
419 } else if *gate && rms >= consts::TRESHOLD_CLOSE {
421 *hold_frames = consts::HOLD_FRAMES;
422 }
423
424 if !*gate
426 {
427 preroll.push_back(frame.clone());
428 if preroll.len() > 3
429 {
430 preroll.pop_front();
431 }
432 }
433
434 if *gate
436 {
437 LOCAL_DISPLAY_HOLD.store((consts::SAMPLE_RATE * consts::DISPLAY_HOLD as u32 / 1000) as usize, Ordering::Relaxed);
438 transmit_audio(&opus_encoder, &frame, &mut encoded_buffer, id, &send_socket);
439 }
440 }
441 }, |_| {}, None).unwrap();
442
443 let output_channels = output_config.channels as usize;
445 let output_source_rate = consts::SAMPLE_RATE as f32;
446 let output_target_rate = output_config.sample_rate as f32;
447
448 let output_resample_step = output_source_rate / output_target_rate;
450
451 let output_stream = output_device.build_output_stream(&output_config, move |data: &mut [f32], _: &_|
453 {
454 if AUDIO_GENERATION.load(Ordering::Relaxed) != current_generation { return; }
456
457 data.fill(0.);
459
460 let frames_to_write = data.len() / output_channels;
461 let mut consumers_guard = CONSUMERS.lock().unwrap();
462
463 for i in 0..frames_to_write
464 {
465 let mut mixed_sample = 0.;
466 let mut active_speakers = 0;
467
468 for (stream, _) in consumers_guard.values_mut()
469 {
470 while stream.resample_pos >= 1.
472 {
473 stream.current_sample = stream.next_sample;
474 stream.next_sample = stream.consumer.try_pop().unwrap_or(0.); stream.resample_pos -= 1.;
476 }
477
478 let interpolated = stream.current_sample + (stream.next_sample - stream.current_sample) * stream.resample_pos;
480 stream.resample_pos += output_resample_step; if interpolated.abs() > consts::MIXING_TRESHOLD
484 {
485 stream.activity_hold = consts::ACTIVITY_HOLD; stream.display_hold = (consts::SAMPLE_RATE * consts::DISPLAY_HOLD as u32 / 1000) as usize; }
488
489 if stream.activity_hold > 0
490 {
491 mixed_sample += interpolated;
493 active_speakers += 1;
494 stream.activity_hold -= 1;
495 }
496
497 if stream.display_hold > 0
499 {
500 stream.display_hold -= 1;
501 }
502 }
503
504 if active_speakers > 1
506 {
507 mixed_sample /= (active_speakers as f32).sqrt();
508 }
509
510 sfx::play_effects(&mut mixed_sample);
512
513 mixed_sample = mixed_sample.tanh();
515
516 for channel in 0..output_channels
518 {
519 data[i * output_channels + channel] = mixed_sample;
520 }
521 }
522 }, |_| {}, None).unwrap();
523
524 input_stream.play().unwrap(); output_stream.play().unwrap(); *LOCAL_STREAMS.lock().unwrap() = Some(LocalStream
530 {
531 _input: input_stream,
532 _output: output_stream,
533 });
534
535 sfx::clear_effects();
537 sfx::queue_effect(SoundEffect::Join);
538
539 let vad_socket = socket.clone();
541 thread::spawn(move ||
542 {
543 let mut iteration_counter = 0u8;
544
545 loop
546 {
547 if !options::get_use_voice()
549 {
550 tx.send(ClientEvent::VoiceActivity(Vec::new())).unwrap(); return;
552 }
553
554 iteration_counter += 1; display_active_speakers(&username, &tx);
558
559 if iteration_counter == 10
561 {
562 voice::send(&vad_socket, VoicePacket
563 {
564 id: Some(id),
565 code: Some(VoiceCode::PING),
566 timestamp: Some(SystemTime::now().duration_since(UNIX_EPOCH).unwrap().as_millis()),
567
568 ..Default::default()
569 }, &chat_options::get_keys().unwrap()).unwrap();
570
571 iteration_counter = 0;
573 }
574
575 thread::sleep(Duration::from_millis(100));
576 }
577 });
578
579 let mut decoded_buffer = [0.0f32; consts::FRAME_SIZE];
581
582 loop
583 {
584 let (network_buffer, _) = match voice::receive(&socket)
586 {
587 Some(r) => r,
588 None => {
590 if AUDIO_GENERATION.load(Ordering::Relaxed) != current_generation { return; }
592
593 sfx::queue_effect(SoundEffect::Leave);
595
596 while sfx::is_playing()
598 {
599 if AUDIO_GENERATION.load(Ordering::Relaxed) != current_generation { return; }
601
602 thread::sleep(Duration::from_millis(50));
603 }
604
605 return;
606 }
607 };
608
609 if network_buffer.seq <= options::get_server_seq() { continue; } options::set_server_seq(network_buffer.seq); let sender_id = match network_buffer.id
615 {
616 Some(id) => id,
617 None => continue
618 };
619
620 if !CONSUMERS.lock().unwrap().contains_key(&sender_id)
622 {
623 add_consumer(sender_id, network_buffer.username.unwrap());
624 }
625
626 if let Some((stream, peer)) = CONSUMERS.lock().unwrap().get_mut(&sender_id)
627 {
628 if let Some(code) = network_buffer.code && let Some(timestamp) = network_buffer.timestamp
630 {
631 match code
632 {
633 VoiceCode::PING =>
635 {
636 voice::send(&socket, VoicePacket
638 {
639 id: Some(id),
640 target_id: Some(sender_id),
641 code: Some(VoiceCode::PONG),
642 timestamp: Some(timestamp),
643
644 ..Default::default()
645 }, &chat_options::get_keys().unwrap()).unwrap();
646 },
647
648 VoiceCode::PONG =>
650 {
651 let latency = SystemTime::now().duration_since(UNIX_EPOCH).unwrap().as_millis().saturating_sub(timestamp);
653
654 stream.latencies.push_back(latency);
656 if stream.latencies.len() > 20 {
658 stream.latencies.pop_front();
659 }
660
661 let sum: u128 = stream.latencies.iter().sum();
663 if !stream.latencies.is_empty()
664 {
665 stream.avg_latency = sum / stream.latencies.len() as u128;
667 }
668 }
669 }
670 }
671
672 if network_buffer.voice.is_none() { continue; }
674
675 if chat_options::is_muted(Some(sender_id)) { continue; }
677
678 if let Ok(decoded_len) = peer.decoder.decode_float(network_buffer.voice.as_deref(), &mut decoded_buffer[..], false)
680 {
681 peer.producer.push_slice(&decoded_buffer[..decoded_len]);
683 }
684 }
685 }
686}
687
688pub fn remove_consumer(id: &usize)
689{
690 if CONSUMERS.lock().unwrap().remove(id).is_some()
691 {
692 sfx::queue_effect(SoundEffect::Leave);
694 }
695}
696
697pub fn remove_all_consumers()
698{
699 CONSUMERS.lock().unwrap().clear();
700
701 sfx::queue_effect(SoundEffect::Join);
703}
704
705pub fn add_consumer(id: usize, username: String)
706{
707 let decoder = Decoder::new
709 (
710 <SampleRate as TryFrom<i32>>::try_from(consts::SAMPLE_RATE as i32).unwrap(),
711 Channels::Mono,
712 ).unwrap();
713
714 let rb = HeapRb::<f32>::new(consts::FRAME_SIZE * consts::JITTER_BUFFER_SIZE);
716 let (producer, mut consumer) = rb.split();
717
718 let first_sample = consumer.try_pop().unwrap_or(0.0);
719
720 CONSUMERS.lock().unwrap().insert(id, (RemoteStream
722 {
723 consumer: consumer,
724 resample_pos: 0.,
725 current_sample: 0.,
726 next_sample: first_sample,
727 activity_hold: 0,
728 display_hold: 0,
729 username: username,
730 latencies: VecDeque::with_capacity(20),
731 avg_latency: 0,
732 }, PeerData
733 {
734 decoder: decoder,
735 producer: producer,
736 }));
737
738 sfx::queue_effect(SoundEffect::Join);
740}
741
742fn display_active_speakers(local_username: &str, tx: &Sender<ClientEvent>)
743{
744 let mut users_to_display = Vec::new();
746
747 let local_speaking = LOCAL_DISPLAY_HOLD.load(Ordering::Relaxed) > 0;
749 users_to_display.push(VoiceUser
750 {
751 id: 0,
752 username: local_username.to_string(),
753 is_speaking: local_speaking,
754 latency: 0,
755 is_local: true,
756 });
757
758 if let Ok(consumers) = CONSUMERS.try_lock()
760 {
761 for (id, (stream, _)) in consumers.iter()
762 {
763 users_to_display.push(VoiceUser
764 {
765 id: *id,
766 username: stream.username.clone(),
767 is_speaking: stream.display_hold > 0, latency: stream.avg_latency,
769 is_local: false,
770 });
771 }
772 }
773
774 if users_to_display.len() > 1
776 {
777 users_to_display[1..].sort_by_key(|u| u.id);
778 }
779
780 tx.send(ClientEvent::VoiceActivity(users_to_display)).unwrap();
782}