Skip to main content

voirs_conversion/
gaming.rs

1//! Gaming engine integration for voice conversion
2//!
3//! This module provides comprehensive integration with major gaming engines,
4//! enabling real-time voice conversion within games for character voices,
5//! player voice chat, and immersive voice experiences.
6//!
7//! ## Supported Engines
8//!
9//! - **Unity**: C# bindings and Unity-specific optimizations
10//! - **Unreal Engine**: Blueprint integration and C++ bindings
11//! - **Godot**: GDScript bindings and native extensions
12//! - **Bevy**: Rust-native integration with ECS systems
13//! - **Custom Engines**: Generic C API for custom game engines
14//!
15//! ## Features
16//!
17//! - **Real-time Voice Conversion**: Ultra-low latency for game voice chat
18//! - **Character Voice Synthesis**: Dynamic NPC voice generation
19//! - **Player Voice Morphing**: Real-time player voice transformation
20//! - **Spatial Audio Integration**: 3D positional voice conversion
21//! - **Performance Optimization**: Game-specific performance targets
22//! - **Memory Management**: Efficient memory usage for game constraints
23//!
24//! ## Usage
25//!
26//! ```rust
27//! # use voirs_conversion::gaming::{GameEngine, GameVoiceProcessor, GameAudioConfig};
28//! # #[tokio::main]
29//! # async fn main() -> Result<(), Box<dyn std::error::Error>> {
30//! // Create processor for Unity
31//! let config = GameAudioConfig::unity_optimized();
32//! let mut processor = GameVoiceProcessor::new(GameEngine::Unity, config)?;
33//!
34//! // Process voice in real-time
35//! let input_audio = vec![0.0f32; 1024]; // Sample audio data
36//! let converted_audio = processor.process_game_voice(&input_audio, "hero_voice").await?;
37//! # Ok(())
38//! # }
39//! ```
40
41use crate::{
42    config::ConversionConfig,
43    core::VoiceConverter,
44    processing::{AudioBuffer, ProcessingPipeline},
45    realtime::{RealtimeConfig, RealtimeConverter},
46    types::{ConversionRequest, ConversionTarget, ConversionType, VoiceCharacteristics},
47    Error, Result,
48};
49use serde::{Deserialize, Serialize};
50use std::collections::HashMap;
51use std::sync::Arc;
52use tokio::sync::RwLock;
53use tracing::{debug, info, warn};
54
55/// Supported gaming engines
56#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
57pub enum GameEngine {
58    /// Unity 3D Engine
59    Unity,
60    /// Unreal Engine 4/5
61    Unreal,
62    /// Godot Engine
63    Godot,
64    /// Bevy Engine (Rust)
65    Bevy,
66    /// Custom/Generic Engine
67    Custom,
68}
69
70impl GameEngine {
71    /// Get engine-specific performance constraints
72    pub fn performance_constraints(&self) -> GamePerformanceConstraints {
73        match self {
74            GameEngine::Unity => GamePerformanceConstraints {
75                max_latency_ms: 20.0,
76                max_cpu_usage_percent: 15.0,
77                max_memory_mb: 64,
78                target_fps_impact: 2.0,
79                audio_thread_priority: ThreadPriority::High,
80            },
81            GameEngine::Unreal => GamePerformanceConstraints {
82                max_latency_ms: 16.0, // 60 FPS target
83                max_cpu_usage_percent: 12.0,
84                max_memory_mb: 128,
85                target_fps_impact: 1.5,
86                audio_thread_priority: ThreadPriority::RealTime,
87            },
88            GameEngine::Godot => GamePerformanceConstraints {
89                max_latency_ms: 25.0,
90                max_cpu_usage_percent: 18.0,
91                max_memory_mb: 48,
92                target_fps_impact: 3.0,
93                audio_thread_priority: ThreadPriority::High,
94            },
95            GameEngine::Bevy => GamePerformanceConstraints {
96                max_latency_ms: 16.0,
97                max_cpu_usage_percent: 10.0,
98                max_memory_mb: 96,
99                target_fps_impact: 1.0,
100                audio_thread_priority: ThreadPriority::High,
101            },
102            GameEngine::Custom => GamePerformanceConstraints {
103                max_latency_ms: 30.0,
104                max_cpu_usage_percent: 20.0,
105                max_memory_mb: 80,
106                target_fps_impact: 4.0,
107                audio_thread_priority: ThreadPriority::Normal,
108            },
109        }
110    }
111
112    /// Get engine name as string
113    pub fn as_str(&self) -> &'static str {
114        match self {
115            GameEngine::Unity => "Unity",
116            GameEngine::Unreal => "Unreal",
117            GameEngine::Godot => "Godot",
118            GameEngine::Bevy => "Bevy",
119            GameEngine::Custom => "Custom",
120        }
121    }
122}
123
124/// Thread priority levels for gaming engines
125#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
126pub enum ThreadPriority {
127    /// Low priority for background tasks
128    Low,
129    /// Normal priority for standard processing
130    Normal,
131    /// High priority for time-sensitive operations
132    High,
133    /// Real-time priority for critical audio processing
134    RealTime,
135}
136
137/// Performance constraints for gaming engines
138#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
139pub struct GamePerformanceConstraints {
140    /// Maximum acceptable latency in milliseconds
141    pub max_latency_ms: f32,
142    /// Maximum CPU usage percentage
143    pub max_cpu_usage_percent: f32,
144    /// Maximum memory usage in MB
145    pub max_memory_mb: u32,
146    /// Target FPS impact (lower is better)
147    pub target_fps_impact: f32,
148    /// Audio processing thread priority
149    pub audio_thread_priority: ThreadPriority,
150}
151
152/// Game-specific audio configuration
153#[derive(Debug, Clone, Serialize, Deserialize)]
154pub struct GameAudioConfig {
155    /// Target gaming engine
156    pub engine: GameEngine,
157    /// Audio buffer size for low latency
158    pub buffer_size: usize,
159    /// Sample rate (typically 44.1kHz or 48kHz)
160    pub sample_rate: u32,
161    /// Number of audio channels
162    pub channels: u32,
163    /// Enable spatial audio processing
164    pub spatial_audio: bool,
165    /// Enable voice activity detection
166    pub voice_activity_detection: bool,
167    /// Enable automatic gain control
168    pub automatic_gain_control: bool,
169    /// Enable noise suppression
170    pub noise_suppression: bool,
171    /// Quality vs performance trade-off (0.0-1.0)
172    pub quality_level: f32,
173    /// Enable adaptive quality based on performance
174    pub adaptive_quality: bool,
175    /// Game-specific optimizations
176    pub engine_optimizations: HashMap<String, f32>,
177}
178
179impl GameAudioConfig {
180    /// Create Unity-optimized configuration
181    pub fn unity_optimized() -> Self {
182        let mut engine_optimizations = HashMap::new();
183        engine_optimizations.insert("unity_audio_clip_compatibility".to_string(), 1.0);
184        engine_optimizations.insert("unity_mixer_integration".to_string(), 1.0);
185        engine_optimizations.insert("unity_audio_source_optimization".to_string(), 0.8);
186
187        Self {
188            engine: GameEngine::Unity,
189            buffer_size: 512,
190            sample_rate: 44100,
191            channels: 2,
192            spatial_audio: true,
193            voice_activity_detection: true,
194            automatic_gain_control: true,
195            noise_suppression: true,
196            quality_level: 0.8,
197            adaptive_quality: true,
198            engine_optimizations,
199        }
200    }
201
202    /// Create Unreal Engine-optimized configuration
203    pub fn unreal_optimized() -> Self {
204        let mut engine_optimizations = HashMap::new();
205        engine_optimizations.insert("unreal_sound_cue_integration".to_string(), 1.0);
206        engine_optimizations.insert("unreal_audio_component_optimization".to_string(), 1.0);
207        engine_optimizations.insert("unreal_metasound_compatibility".to_string(), 0.9);
208
209        Self {
210            engine: GameEngine::Unreal,
211            buffer_size: 256, // Lower latency for Unreal
212            sample_rate: 48000,
213            channels: 2,
214            spatial_audio: true,
215            voice_activity_detection: true,
216            automatic_gain_control: true,
217            noise_suppression: true,
218            quality_level: 0.9,
219            adaptive_quality: true,
220            engine_optimizations,
221        }
222    }
223
224    /// Create Godot-optimized configuration
225    pub fn godot_optimized() -> Self {
226        let mut engine_optimizations = HashMap::new();
227        engine_optimizations.insert("godot_audio_stream_integration".to_string(), 1.0);
228        engine_optimizations.insert("godot_audio_bus_optimization".to_string(), 0.8);
229
230        Self {
231            engine: GameEngine::Godot,
232            buffer_size: 1024,
233            sample_rate: 44100,
234            channels: 2,
235            spatial_audio: true,
236            voice_activity_detection: true,
237            automatic_gain_control: false, // Godot handles this
238            noise_suppression: true,
239            quality_level: 0.7,
240            adaptive_quality: true,
241            engine_optimizations,
242        }
243    }
244
245    /// Create Bevy-optimized configuration
246    pub fn bevy_optimized() -> Self {
247        let mut engine_optimizations = HashMap::new();
248        engine_optimizations.insert("bevy_audio_resource_integration".to_string(), 1.0);
249        engine_optimizations.insert("bevy_ecs_system_optimization".to_string(), 1.0);
250
251        Self {
252            engine: GameEngine::Bevy,
253            buffer_size: 256,
254            sample_rate: 44100,
255            channels: 2,
256            spatial_audio: true,
257            voice_activity_detection: true,
258            automatic_gain_control: true,
259            noise_suppression: true,
260            quality_level: 0.85,
261            adaptive_quality: true,
262            engine_optimizations,
263        }
264    }
265}
266
267/// Game voice processing modes
268#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
269pub enum GameVoiceMode {
270    /// Player voice chat
271    PlayerChat,
272    /// NPC character voice
273    CharacterVoice,
274    /// Environmental voice effects
275    EnvironmentalVoice,
276    /// Narrator voice
277    NarratorVoice,
278    /// Dynamic voice morphing
279    DynamicMorphing,
280}
281
282/// Game voice processor for real-time voice conversion in games
283#[derive(Debug)]
284pub struct GameVoiceProcessor {
285    /// Target gaming engine
286    engine: GameEngine,
287    /// Game audio configuration
288    config: GameAudioConfig,
289    /// Real-time voice converter
290    realtime_converter: RealtimeConverter,
291    /// Voice converter for complex transformations
292    voice_converter: Arc<VoiceConverter>,
293    /// Performance constraints
294    constraints: GamePerformanceConstraints,
295    /// Character voice presets
296    character_presets: Arc<RwLock<HashMap<String, VoiceCharacteristics>>>,
297    /// Active voice sessions
298    active_sessions: Arc<RwLock<HashMap<String, GameVoiceSession>>>,
299    /// Performance monitor
300    performance_monitor: GamePerformanceMonitor,
301}
302
303impl GameVoiceProcessor {
304    /// Create new game voice processor
305    pub fn new(engine: GameEngine, config: GameAudioConfig) -> Result<Self> {
306        let constraints = engine.performance_constraints();
307
308        // Create real-time converter with game-optimized settings
309        let realtime_config = RealtimeConfig {
310            buffer_size: config.buffer_size,
311            sample_rate: config.sample_rate,
312            target_latency_ms: constraints.max_latency_ms,
313            overlap_factor: 0.25,
314            adaptive_buffering: config.adaptive_quality,
315            max_threads: 2,          // Conservative for games
316            enable_lookahead: false, // Disable for lowest latency
317            lookahead_size: 0,
318        };
319
320        let realtime_converter = RealtimeConverter::new(realtime_config)?;
321        let voice_converter = Arc::new(VoiceConverter::new()?);
322
323        Ok(Self {
324            engine,
325            config,
326            realtime_converter,
327            voice_converter,
328            constraints,
329            character_presets: Arc::new(RwLock::new(HashMap::new())),
330            active_sessions: Arc::new(RwLock::new(HashMap::new())),
331            performance_monitor: GamePerformanceMonitor::new(),
332        })
333    }
334
335    /// Process game voice in real-time
336    pub async fn process_game_voice(
337        &mut self,
338        input_audio: &[f32],
339        voice_id: &str,
340    ) -> Result<Vec<f32>> {
341        let start_time = std::time::Instant::now();
342
343        // Check performance constraints
344        if !self
345            .performance_monitor
346            .check_performance_budget(&self.constraints)
347        {
348            warn!("Performance budget exceeded, using passthrough mode");
349            return Ok(input_audio.to_vec());
350        }
351
352        // Get voice characteristics
353        let voice_characteristics = {
354            let presets = self.character_presets.read().await;
355            presets.get(voice_id).cloned().unwrap_or_default()
356        };
357
358        // Set conversion target
359        let target = ConversionTarget::new(voice_characteristics);
360        self.realtime_converter.set_conversion_target(target);
361
362        // Process with real-time converter
363        let result = self.realtime_converter.process_chunk(input_audio).await?;
364
365        // Update performance metrics
366        let processing_time = start_time.elapsed();
367        self.performance_monitor.record_processing(
368            processing_time,
369            input_audio.len(),
370            &self.constraints,
371        );
372
373        debug!(
374            "Game voice processed: {} samples in {:.2}ms",
375            input_audio.len(),
376            processing_time.as_secs_f32() * 1000.0
377        );
378
379        Ok(result)
380    }
381
382    /// Process character voice with specific mode
383    pub async fn process_character_voice(
384        &mut self,
385        input_audio: &[f32],
386        character_id: &str,
387        mode: GameVoiceMode,
388    ) -> Result<Vec<f32>> {
389        // Apply mode-specific processing
390        let processed_audio = match mode {
391            GameVoiceMode::PlayerChat => self.apply_player_chat_processing(input_audio).await?,
392            GameVoiceMode::CharacterVoice => {
393                self.apply_character_voice_processing(input_audio, character_id)
394                    .await?
395            }
396            GameVoiceMode::EnvironmentalVoice => {
397                self.apply_environmental_processing(input_audio).await?
398            }
399            GameVoiceMode::NarratorVoice => self.apply_narrator_processing(input_audio).await?,
400            GameVoiceMode::DynamicMorphing => {
401                self.apply_dynamic_morphing(input_audio, character_id)
402                    .await?
403            }
404        };
405
406        Ok(processed_audio)
407    }
408
409    /// Register character voice preset
410    pub async fn register_character_preset(
411        &self,
412        character_id: String,
413        characteristics: VoiceCharacteristics,
414    ) {
415        let mut presets = self.character_presets.write().await;
416        presets.insert(character_id.clone(), characteristics);
417        info!("Registered character preset: {}", character_id);
418    }
419
420    /// Start voice session for persistent processing
421    pub async fn start_voice_session(
422        &self,
423        session_id: String,
424        character_id: String,
425        mode: GameVoiceMode,
426    ) -> Result<()> {
427        let session = GameVoiceSession {
428            session_id: session_id.clone(),
429            character_id,
430            mode,
431            start_time: std::time::Instant::now(),
432            processed_samples: 0,
433            latency_samples: Vec::new(),
434        };
435
436        let mut sessions = self.active_sessions.write().await;
437        sessions.insert(session_id.clone(), session);
438
439        info!("Started voice session: {}", session_id);
440        Ok(())
441    }
442
443    /// Stop voice session
444    pub async fn stop_voice_session(&self, session_id: &str) -> Result<GameVoiceSession> {
445        let mut sessions = self.active_sessions.write().await;
446        sessions
447            .remove(session_id)
448            .ok_or_else(|| Error::processing(format!("Voice session not found: {session_id}")))
449    }
450
451    /// Get current performance metrics
452    pub fn get_performance_metrics(&self) -> GamePerformanceMetrics {
453        self.performance_monitor.get_current_metrics()
454    }
455
456    /// Check if processing meets game performance requirements
457    pub fn is_performance_acceptable(&self) -> bool {
458        self.performance_monitor
459            .check_performance_budget(&self.constraints)
460    }
461
462    /// Get engine-specific integration info
463    pub fn get_engine_integration_info(&self) -> GameEngineIntegration {
464        match self.engine {
465            GameEngine::Unity => GameEngineIntegration::Unity(UnityIntegration {
466                plugin_version: "1.0.0".to_string(),
467                unity_version_compatibility: "2021.3+".to_string(),
468                audio_clip_support: true,
469                mixer_integration: true,
470                blueprint_support: false,
471            }),
472            GameEngine::Unreal => GameEngineIntegration::Unreal(UnrealIntegration {
473                plugin_version: "1.0.0".to_string(),
474                unreal_version_compatibility: "4.27+, 5.0+".to_string(),
475                blueprint_support: true,
476                metasound_support: true,
477                audio_component_integration: true,
478            }),
479            GameEngine::Godot => GameEngineIntegration::Godot(GodotIntegration {
480                plugin_version: "1.0.0".to_string(),
481                godot_version_compatibility: "3.5+, 4.0+".to_string(),
482                gdscript_bindings: true,
483                audio_stream_support: true,
484            }),
485            GameEngine::Bevy => GameEngineIntegration::Bevy(BevyIntegration {
486                plugin_version: "1.0.0".to_string(),
487                bevy_version_compatibility: "0.11+".to_string(),
488                ecs_integration: true,
489                audio_resource_support: true,
490            }),
491            GameEngine::Custom => GameEngineIntegration::Custom(CustomIntegration {
492                c_api_version: "1.0.0".to_string(),
493                supported_platforms: vec![
494                    "Windows".to_string(),
495                    "macOS".to_string(),
496                    "Linux".to_string(),
497                ],
498                callback_support: true,
499            }),
500        }
501    }
502
503    // Private helper methods
504
505    async fn apply_player_chat_processing(&mut self, input_audio: &[f32]) -> Result<Vec<f32>> {
506        // Apply noise suppression and voice enhancement
507        let mut processed = input_audio.to_vec();
508
509        if self.config.noise_suppression {
510            processed = self.apply_noise_suppression(&processed)?;
511        }
512
513        if self.config.automatic_gain_control {
514            processed = self.apply_automatic_gain_control(&processed)?;
515        }
516
517        Ok(processed)
518    }
519
520    async fn apply_character_voice_processing(
521        &mut self,
522        input_audio: &[f32],
523        character_id: &str,
524    ) -> Result<Vec<f32>> {
525        self.process_game_voice(input_audio, character_id).await
526    }
527
528    async fn apply_environmental_processing(&mut self, input_audio: &[f32]) -> Result<Vec<f32>> {
529        // Apply environmental effects (reverb, echo, etc.)
530        let mut processed = input_audio.to_vec();
531
532        // Apply environmental reverb
533        for sample in processed.iter_mut() {
534            *sample *= 0.7; // Reduce direct signal
535        }
536
537        Ok(processed)
538    }
539
540    async fn apply_narrator_processing(&mut self, input_audio: &[f32]) -> Result<Vec<f32>> {
541        // Apply narrator-specific processing (clarity, authority)
542        let mut processed = input_audio.to_vec();
543
544        // Enhance clarity
545        for sample in processed.iter_mut() {
546            *sample = (*sample * 1.2).clamp(-1.0, 1.0);
547        }
548
549        Ok(processed)
550    }
551
552    async fn apply_dynamic_morphing(
553        &mut self,
554        input_audio: &[f32],
555        character_id: &str,
556    ) -> Result<Vec<f32>> {
557        // Apply time-varying voice morphing
558        self.process_game_voice(input_audio, character_id).await
559    }
560
561    fn apply_noise_suppression(&self, audio: &[f32]) -> Result<Vec<f32>> {
562        // Simple noise gate implementation
563        let threshold = 0.01;
564        let mut processed = audio.to_vec();
565
566        for sample in processed.iter_mut() {
567            if sample.abs() < threshold {
568                *sample *= 0.1;
569            }
570        }
571
572        Ok(processed)
573    }
574
575    fn apply_automatic_gain_control(&self, audio: &[f32]) -> Result<Vec<f32>> {
576        // Simple AGC implementation
577        let target_level = 0.7;
578        let current_level = audio.iter().map(|&x| x.abs()).sum::<f32>() / audio.len() as f32;
579
580        if current_level > 0.0 {
581            let gain = target_level / current_level;
582            let clamped_gain = gain.clamp(0.5, 2.0);
583
584            Ok(audio
585                .iter()
586                .map(|&x| (x * clamped_gain).clamp(-1.0, 1.0))
587                .collect())
588        } else {
589            Ok(audio.to_vec())
590        }
591    }
592}
593
594/// Game voice session for tracking processing state
595#[derive(Debug, Clone)]
596pub struct GameVoiceSession {
597    /// Unique identifier for this voice session
598    pub session_id: String,
599    /// Character or voice preset being used
600    pub character_id: String,
601    /// Voice processing mode for this session
602    pub mode: GameVoiceMode,
603    /// Timestamp when the session started
604    pub start_time: std::time::Instant,
605    /// Total number of audio samples processed in this session
606    pub processed_samples: usize,
607    /// Historical latency measurements in milliseconds
608    pub latency_samples: Vec<f32>,
609}
610
611/// Game performance monitor
612#[derive(Debug)]
613pub struct GamePerformanceMonitor {
614    processing_times: Vec<std::time::Duration>,
615    cpu_usage_samples: Vec<f32>,
616    memory_usage_samples: Vec<u32>,
617    frame_drops: u32,
618    last_check: std::time::Instant,
619}
620
621impl GamePerformanceMonitor {
622    fn new() -> Self {
623        Self {
624            processing_times: Vec::new(),
625            cpu_usage_samples: Vec::new(),
626            memory_usage_samples: Vec::new(),
627            frame_drops: 0,
628            last_check: std::time::Instant::now(),
629        }
630    }
631
632    fn record_processing(
633        &mut self,
634        processing_time: std::time::Duration,
635        _sample_count: usize,
636        constraints: &GamePerformanceConstraints,
637    ) {
638        self.processing_times.push(processing_time);
639
640        // Keep only recent samples
641        if self.processing_times.len() > 100 {
642            self.processing_times.drain(0..50);
643        }
644
645        // Check for frame drops
646        let latency_ms = processing_time.as_secs_f32() * 1000.0;
647        if latency_ms > constraints.max_latency_ms {
648            self.frame_drops += 1;
649        }
650    }
651
652    fn check_performance_budget(&self, constraints: &GamePerformanceConstraints) -> bool {
653        if self.processing_times.is_empty() {
654            return true;
655        }
656
657        let avg_latency_ms = self
658            .processing_times
659            .iter()
660            .map(|d| d.as_secs_f32() * 1000.0)
661            .sum::<f32>()
662            / self.processing_times.len() as f32;
663
664        avg_latency_ms <= constraints.max_latency_ms
665    }
666
667    fn get_current_metrics(&self) -> GamePerformanceMetrics {
668        let avg_latency_ms = if self.processing_times.is_empty() {
669            0.0
670        } else {
671            self.processing_times
672                .iter()
673                .map(|d| d.as_secs_f32() * 1000.0)
674                .sum::<f32>()
675                / self.processing_times.len() as f32
676        };
677
678        GamePerformanceMetrics {
679            average_latency_ms: avg_latency_ms,
680            cpu_usage_percent: self.cpu_usage_samples.last().copied().unwrap_or(0.0),
681            memory_usage_mb: self.memory_usage_samples.last().copied().unwrap_or(0),
682            frame_drops: self.frame_drops,
683            uptime_seconds: self.last_check.elapsed().as_secs(),
684        }
685    }
686}
687
688/// Game performance metrics
689#[derive(Debug, Clone, Serialize, Deserialize)]
690pub struct GamePerformanceMetrics {
691    /// Average processing latency in milliseconds
692    pub average_latency_ms: f32,
693    /// CPU usage as a percentage (0.0-100.0)
694    pub cpu_usage_percent: f32,
695    /// Memory usage in megabytes
696    pub memory_usage_mb: u32,
697    /// Number of frames dropped due to performance issues
698    pub frame_drops: u32,
699    /// Total uptime of the processor in seconds
700    pub uptime_seconds: u64,
701}
702
703/// Engine-specific integration information
704#[derive(Debug, Clone, Serialize, Deserialize)]
705pub enum GameEngineIntegration {
706    /// Unity 3D engine integration details
707    Unity(UnityIntegration),
708    /// Unreal Engine 4/5 integration details
709    Unreal(UnrealIntegration),
710    /// Godot engine integration details
711    Godot(GodotIntegration),
712    /// Bevy engine integration details
713    Bevy(BevyIntegration),
714    /// Custom engine integration details
715    Custom(CustomIntegration),
716}
717
718/// Unity 3D engine integration configuration
719#[derive(Debug, Clone, Serialize, Deserialize)]
720pub struct UnityIntegration {
721    /// Version of the Unity plugin
722    pub plugin_version: String,
723    /// Compatible Unity versions
724    pub unity_version_compatibility: String,
725    /// Whether AudioClip integration is supported
726    pub audio_clip_support: bool,
727    /// Whether Audio Mixer integration is available
728    pub mixer_integration: bool,
729    /// Whether Blueprint support is enabled (always false for Unity)
730    pub blueprint_support: bool,
731}
732
733/// Unreal Engine 4/5 integration configuration
734#[derive(Debug, Clone, Serialize, Deserialize)]
735pub struct UnrealIntegration {
736    /// Version of the Unreal Engine plugin
737    pub plugin_version: String,
738    /// Compatible Unreal Engine versions
739    pub unreal_version_compatibility: String,
740    /// Whether Blueprint visual scripting is supported
741    pub blueprint_support: bool,
742    /// Whether MetaSound audio system is supported
743    pub metasound_support: bool,
744    /// Whether Audio Component integration is available
745    pub audio_component_integration: bool,
746}
747
748/// Godot engine integration configuration
749#[derive(Debug, Clone, Serialize, Deserialize)]
750pub struct GodotIntegration {
751    /// Version of the Godot plugin
752    pub plugin_version: String,
753    /// Compatible Godot versions
754    pub godot_version_compatibility: String,
755    /// Whether GDScript bindings are available
756    pub gdscript_bindings: bool,
757    /// Whether AudioStream integration is supported
758    pub audio_stream_support: bool,
759}
760
761/// Bevy engine integration configuration
762#[derive(Debug, Clone, Serialize, Deserialize)]
763pub struct BevyIntegration {
764    /// Version of the Bevy plugin
765    pub plugin_version: String,
766    /// Compatible Bevy versions
767    pub bevy_version_compatibility: String,
768    /// Whether ECS system integration is available
769    pub ecs_integration: bool,
770    /// Whether Audio resource integration is supported
771    pub audio_resource_support: bool,
772}
773
774/// Custom engine integration configuration
775#[derive(Debug, Clone, Serialize, Deserialize)]
776pub struct CustomIntegration {
777    /// Version of the C API
778    pub c_api_version: String,
779    /// List of supported platforms
780    pub supported_platforms: Vec<String>,
781    /// Whether callback-based integration is supported
782    pub callback_support: bool,
783}
784
785#[cfg(test)]
786mod tests {
787    use super::*;
788
789    #[test]
790    fn test_game_engine_constraints() {
791        let unity_constraints = GameEngine::Unity.performance_constraints();
792        assert!(unity_constraints.max_latency_ms <= 20.0);
793        assert!(unity_constraints.max_cpu_usage_percent <= 15.0);
794
795        let unreal_constraints = GameEngine::Unreal.performance_constraints();
796        assert!(unreal_constraints.max_latency_ms <= 16.0);
797        assert_eq!(
798            unreal_constraints.audio_thread_priority,
799            ThreadPriority::RealTime
800        );
801    }
802
803    #[test]
804    fn test_game_audio_config_creation() {
805        let unity_config = GameAudioConfig::unity_optimized();
806        assert_eq!(unity_config.engine, GameEngine::Unity);
807        assert!(unity_config.spatial_audio);
808        assert!(unity_config.adaptive_quality);
809
810        let unreal_config = GameAudioConfig::unreal_optimized();
811        assert_eq!(unreal_config.engine, GameEngine::Unreal);
812        assert_eq!(unreal_config.buffer_size, 256);
813        assert_eq!(unreal_config.sample_rate, 48000);
814    }
815
816    #[tokio::test]
817    async fn test_game_voice_processor_creation() {
818        let config = GameAudioConfig::unity_optimized();
819        let processor = GameVoiceProcessor::new(GameEngine::Unity, config);
820        assert!(processor.is_ok());
821
822        let processor = processor.unwrap();
823        assert_eq!(processor.engine, GameEngine::Unity);
824    }
825
826    #[tokio::test]
827    async fn test_character_preset_registration() {
828        let config = GameAudioConfig::unity_optimized();
829        let processor = GameVoiceProcessor::new(GameEngine::Unity, config).unwrap();
830
831        let characteristics = VoiceCharacteristics::default();
832        processor
833            .register_character_preset("hero".to_string(), characteristics)
834            .await;
835
836        let presets = processor.character_presets.read().await;
837        assert!(presets.contains_key("hero"));
838    }
839
840    #[tokio::test]
841    async fn test_voice_session_management() {
842        let config = GameAudioConfig::unity_optimized();
843        let processor = GameVoiceProcessor::new(GameEngine::Unity, config).unwrap();
844
845        // Start session
846        let result = processor
847            .start_voice_session(
848                "session1".to_string(),
849                "hero".to_string(),
850                GameVoiceMode::CharacterVoice,
851            )
852            .await;
853        assert!(result.is_ok());
854
855        // Check session exists
856        let sessions = processor.active_sessions.read().await;
857        assert!(sessions.contains_key("session1"));
858    }
859
860    #[test]
861    fn test_game_voice_modes() {
862        let modes = [
863            GameVoiceMode::PlayerChat,
864            GameVoiceMode::CharacterVoice,
865            GameVoiceMode::EnvironmentalVoice,
866            GameVoiceMode::NarratorVoice,
867            GameVoiceMode::DynamicMorphing,
868        ];
869
870        for mode in &modes {
871            // Test serialization
872            let serialized = serde_json::to_string(mode).unwrap();
873            let deserialized: GameVoiceMode = serde_json::from_str(&serialized).unwrap();
874            assert_eq!(*mode, deserialized);
875        }
876    }
877
878    #[test]
879    fn test_performance_monitor() {
880        let mut monitor = GamePerformanceMonitor::new();
881        let constraints = GameEngine::Unity.performance_constraints();
882
883        // Record some processing times
884        monitor.record_processing(std::time::Duration::from_millis(10), 1024, &constraints);
885
886        let metrics = monitor.get_current_metrics();
887        assert!(metrics.average_latency_ms > 0.0);
888        assert!(monitor.check_performance_budget(&constraints));
889    }
890
891    #[test]
892    fn test_engine_integration_info() {
893        let config = GameAudioConfig::unity_optimized();
894        let processor = GameVoiceProcessor::new(GameEngine::Unity, config).unwrap();
895
896        let integration = processor.get_engine_integration_info();
897        match integration {
898            GameEngineIntegration::Unity(unity) => {
899                assert!(unity.audio_clip_support);
900                assert!(unity.mixer_integration);
901            }
902            _ => panic!("Expected Unity integration"),
903        }
904    }
905
906    #[test]
907    fn test_thread_priority_ordering() {
908        assert!(ThreadPriority::RealTime > ThreadPriority::High);
909        assert!(ThreadPriority::High > ThreadPriority::Normal);
910        assert!(ThreadPriority::Normal > ThreadPriority::Low);
911    }
912
913    #[tokio::test]
914    async fn test_noise_suppression() {
915        let config = GameAudioConfig::unity_optimized();
916        let processor = GameVoiceProcessor::new(GameEngine::Unity, config).unwrap();
917
918        let noisy_audio = vec![0.001, 0.5, 0.002, -0.7, 0.003]; // Mix of noise and signal
919        let processed = processor.apply_noise_suppression(&noisy_audio).unwrap();
920
921        // Check that small signals are suppressed
922        assert!(processed[0].abs() < noisy_audio[0].abs());
923        assert!(processed[2].abs() < noisy_audio[2].abs());
924        assert!(processed[4].abs() < noisy_audio[4].abs());
925
926        // Check that large signals are preserved
927        assert_eq!(processed[1], noisy_audio[1]);
928        assert_eq!(processed[3], noisy_audio[3]);
929    }
930
931    #[tokio::test]
932    async fn test_automatic_gain_control() {
933        let config = GameAudioConfig::unity_optimized();
934        let processor = GameVoiceProcessor::new(GameEngine::Unity, config).unwrap();
935
936        let quiet_audio = vec![0.1, -0.1, 0.05, -0.05];
937        let processed = processor
938            .apply_automatic_gain_control(&quiet_audio)
939            .unwrap();
940
941        // Check that gain was applied
942        let original_level =
943            quiet_audio.iter().map(|&x| x.abs()).sum::<f32>() / quiet_audio.len() as f32;
944        let processed_level =
945            processed.iter().map(|&x| x.abs()).sum::<f32>() / processed.len() as f32;
946        assert!(processed_level > original_level);
947    }
948}