feat: replace Tauri with iced for native cross-platform GUI
- Create iced-app crate with pure Rust GUI using iced 0.13 - Implement server list, connect form, channel/user panels, messaging - Implement ServerQuery panel for direct server queries - Add VoiceEngine in tsaudio for decode/buffer/playback pipeline - Update CI to build iced binary (no frontend build step needed) - Remove Tauri dependency from workspace - 69 tests passing across all crates
This commit is contained in:
@@ -0,0 +1,117 @@
|
||||
use crate::buffer::JitterBuffer;
|
||||
use crate::codec::OpusDecoder;
|
||||
use crate::playback::AudioPlayback;
|
||||
use crate::{AudioConfig, AudioError, AudioFrame, AudioResult};
|
||||
|
||||
pub struct VoiceEngine {
|
||||
decoder: OpusDecoder,
|
||||
jitter_buffer: JitterBuffer,
|
||||
playback: AudioPlayback,
|
||||
config: AudioConfig,
|
||||
client_decoders: std::collections::HashMap<u16, OpusDecoder>,
|
||||
}
|
||||
|
||||
impl VoiceEngine {
|
||||
pub fn new(config: AudioConfig) -> AudioResult<Self> {
|
||||
let decoder = OpusDecoder::new(config.sample_rate, config.channels)?;
|
||||
let jitter_buffer = JitterBuffer::with_target_delay(100, 60);
|
||||
let playback = AudioPlayback::new(config.clone());
|
||||
|
||||
Ok(Self {
|
||||
decoder,
|
||||
jitter_buffer,
|
||||
playback,
|
||||
config,
|
||||
client_decoders: std::collections::HashMap::new(),
|
||||
})
|
||||
}
|
||||
|
||||
pub fn start(&mut self) -> AudioResult<()> {
|
||||
self.playback.start()
|
||||
}
|
||||
|
||||
pub fn stop(&mut self) -> AudioResult<()> {
|
||||
self.playback.stop()
|
||||
}
|
||||
|
||||
pub fn process_voice_data(
|
||||
&mut self,
|
||||
client_id: u16,
|
||||
codec: u8,
|
||||
packet_id: u16,
|
||||
audio_data: &[u8],
|
||||
) -> AudioResult<()> {
|
||||
let decoder = self.client_decoders.entry(client_id).or_insert_with(|| {
|
||||
OpusDecoder::new(self.config.sample_rate, self.config.channels)
|
||||
.unwrap_or_else(|_| {
|
||||
OpusDecoder::new(48000, 1).unwrap()
|
||||
})
|
||||
});
|
||||
|
||||
let samples = decoder.decode_packet(audio_data)?;
|
||||
|
||||
let frame = AudioFrame::new(self.config.sample_rate, self.config.channels, samples)
|
||||
.with_sequence(packet_id)
|
||||
.with_codec(codec);
|
||||
|
||||
self.jitter_buffer.push(frame)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn output_tick(&mut self) -> AudioResult<()> {
|
||||
if let Some(frame) = self.jitter_buffer.pop() {
|
||||
self.playback.play(frame)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn run_output_loop(&mut self) -> AudioResult<()> {
|
||||
loop {
|
||||
self.output_tick()?;
|
||||
std::thread::sleep(std::time::Duration::from_millis(5));
|
||||
}
|
||||
}
|
||||
|
||||
pub fn list_output_devices() -> AudioResult<Vec<String>> {
|
||||
AudioPlayback::list_devices()
|
||||
}
|
||||
|
||||
pub fn buffered_ms(&self) -> u32 {
|
||||
self.jitter_buffer.buffered_ms()
|
||||
}
|
||||
|
||||
pub fn jitter_buffer_len(&self) -> usize {
|
||||
self.jitter_buffer.len()
|
||||
}
|
||||
|
||||
pub fn playback_buffer_len(&self) -> usize {
|
||||
self.playback.buffer_len()
|
||||
}
|
||||
|
||||
pub fn clear_jitter_buffer(&mut self) {
|
||||
self.jitter_buffer.clear();
|
||||
}
|
||||
|
||||
pub fn remove_client(&mut self, client_id: u16) {
|
||||
self.client_decoders.remove(&client_id);
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_voice_engine_new() {
|
||||
let config = AudioConfig::default();
|
||||
let engine = VoiceEngine::new(config);
|
||||
assert!(engine.is_ok());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_voice_engine_list_devices() {
|
||||
let devices = VoiceEngine::list_output_devices();
|
||||
assert!(devices.is_ok());
|
||||
}
|
||||
}
|
||||
@@ -3,12 +3,14 @@
|
||||
pub mod buffer;
|
||||
pub mod capture;
|
||||
pub mod codec;
|
||||
pub mod engine;
|
||||
pub mod playback;
|
||||
pub mod vad;
|
||||
|
||||
pub use buffer::*;
|
||||
pub use capture::*;
|
||||
pub use codec::*;
|
||||
pub use engine::*;
|
||||
pub use playback::*;
|
||||
pub use vad::*;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user