Skip to main content

voirs_conversion/
fallback.rs

1//! Graceful degradation and fallback strategies for voice conversion
2//!
3//! This module provides robust error recovery mechanisms and quality-based fallback
4//! strategies to ensure the system continues to function even when optimal conversion
5//! fails or produces poor quality results.
6
7use crate::{
8    config::ConversionConfig,
9    quality::ArtifactDetector,
10    transforms::{PitchTransform, SpeedTransform, Transform},
11    types::{
12        ConversionRequest, ConversionResult, ConversionTarget, ConversionType, DetectedArtifacts,
13        ObjectiveQualityMetrics, VoiceCharacteristics,
14    },
15    Error, Result,
16};
17use serde::{Deserialize, Serialize};
18use std::collections::HashMap;
19use std::time::{Duration, Instant, SystemTime};
20use tracing::{debug, error, info, warn};
21
22/// Graceful degradation controller
23#[derive(Debug)]
24pub struct GracefulDegradationController {
25    /// Configuration for degradation strategies
26    config: DegradationConfig,
27    /// Quality thresholds for different fallback levels
28    quality_thresholds: QualityThresholds,
29    /// Fallback strategy executor
30    strategy_executor: FallbackStrategyExecutor,
31    /// Performance metrics tracker
32    performance_tracker: PerformanceTracker,
33    /// Failure history for learning
34    failure_history: FailureHistory,
35}
36
37/// Configuration for graceful degradation
38#[derive(Debug, Clone)]
39pub struct DegradationConfig {
40    /// Enable graceful degradation
41    pub enable_degradation: bool,
42    /// Enable quality-based fallbacks
43    pub enable_quality_fallbacks: bool,
44    /// Enable error recovery attempts
45    pub enable_error_recovery: bool,
46    /// Maximum number of retry attempts
47    pub max_retry_attempts: u32,
48    /// Maximum time to spend on recovery attempts
49    pub max_recovery_time_ms: u64,
50    /// Enable fallback learning
51    pub enable_learning: bool,
52    /// Enable performance monitoring
53    pub enable_performance_monitoring: bool,
54}
55
56impl Default for DegradationConfig {
57    fn default() -> Self {
58        Self {
59            enable_degradation: true,
60            enable_quality_fallbacks: true,
61            enable_error_recovery: true,
62            max_retry_attempts: 3,
63            max_recovery_time_ms: 5000,
64            enable_learning: true,
65            enable_performance_monitoring: true,
66        }
67    }
68}
69
70/// Quality thresholds for triggering different fallback strategies
71#[derive(Debug, Clone)]
72pub struct QualityThresholds {
73    /// Minimum acceptable quality score (0.0 to 1.0)
74    pub min_acceptable_quality: f32,
75    /// Quality threshold for reducing conversion complexity
76    pub complexity_reduction_threshold: f32,
77    /// Quality threshold for switching to simpler algorithms
78    pub simple_algorithm_threshold: f32,
79    /// Quality threshold for passthrough fallback
80    pub passthrough_threshold: f32,
81    /// Maximum acceptable artifact score
82    pub max_artifact_score: f32,
83}
84
85impl Default for QualityThresholds {
86    fn default() -> Self {
87        Self {
88            min_acceptable_quality: 0.3,
89            complexity_reduction_threshold: 0.5,
90            simple_algorithm_threshold: 0.4,
91            passthrough_threshold: 0.2,
92            max_artifact_score: 0.7,
93        }
94    }
95}
96
97/// Fallback strategy executor
98#[derive(Debug)]
99pub struct FallbackStrategyExecutor {
100    /// Available fallback strategies
101    strategies: Vec<Box<dyn FallbackStrategy>>,
102    /// Strategy performance history
103    strategy_performance: HashMap<String, StrategyPerformance>,
104}
105
106/// Fallback strategy trait  
107pub trait FallbackStrategy: Send + Sync + std::fmt::Debug {
108    /// Name of the strategy
109    fn name(&self) -> &str;
110
111    /// Check if this strategy can handle the given failure
112    fn can_handle(&self, failure_type: &FailureType, conversion_type: &ConversionType) -> bool;
113
114    /// Estimate the success probability for this strategy
115    fn success_probability(&self, context: &FallbackContext) -> f32;
116
117    /// Apply the fallback strategy
118    fn apply(
119        &self,
120        request: &ConversionRequest,
121        context: &FallbackContext,
122        config: &ConversionConfig,
123    ) -> Result<ConversionResult>;
124
125    /// Get strategy priority (higher = more preferred)
126    fn priority(&self) -> i32;
127}
128
129/// Context for fallback decisions
130#[derive(Debug, Clone)]
131pub struct FallbackContext {
132    /// Original error that triggered fallback
133    pub original_error: Option<String>,
134    /// Current quality metrics
135    pub current_quality: Option<f32>,
136    /// Detected artifacts
137    pub artifacts: Option<DetectedArtifacts>,
138    /// Previous attempt results
139    pub previous_attempts: Vec<FallbackAttempt>,
140    /// Available processing resources
141    pub available_resources: ResourceContext,
142    /// Time constraints
143    pub time_constraints: TimeConstraints,
144}
145
146/// Previous fallback attempt
147#[derive(Debug, Clone)]
148pub struct FallbackAttempt {
149    /// Name of the fallback strategy that was attempted
150    pub strategy_name: String,
151    /// Whether the fallback attempt was successful
152    pub success: bool,
153    /// Quality score achieved by this fallback attempt if successful
154    pub quality_achieved: Option<f32>,
155    /// Time taken to execute this fallback attempt
156    pub processing_time: Duration,
157    /// Error message if the fallback attempt failed
158    pub error: Option<String>,
159}
160
161/// Available processing resources
162#[derive(Debug, Clone)]
163pub struct ResourceContext {
164    /// Current CPU utilization as a percentage (0.0 to 100.0)
165    pub cpu_usage_percent: f32,
166    /// Available system memory in megabytes
167    pub memory_available_mb: f64,
168    /// Whether GPU acceleration is available for processing
169    pub gpu_available: bool,
170    /// Overall processing capacity normalized to 0.0 to 1.0 scale
171    pub processing_capacity: f32, // 0.0 to 1.0
172}
173
174/// Time constraints for fallback processing
175#[derive(Debug, Clone)]
176pub struct TimeConstraints {
177    /// Maximum allowed time for fallback processing to complete
178    pub max_processing_time: Duration,
179    /// Optional absolute deadline for fallback completion
180    pub deadline: Option<Instant>,
181    /// Whether real-time processing constraints must be met
182    pub real_time_requirement: bool,
183}
184
185/// Types of failures that can trigger fallbacks
186#[derive(Debug, Clone, PartialEq, Eq, Hash)]
187pub enum FailureType {
188    /// Processing error
189    ProcessingError,
190    /// Quality below threshold
191    QualityFailure,
192    /// High artifact levels
193    ArtifactFailure,
194    /// Timeout or performance issue
195    PerformanceFailure,
196    /// Resource exhaustion
197    ResourceFailure,
198    /// Model loading failure
199    ModelFailure,
200    /// Memory allocation failure
201    MemoryFailure,
202    /// Unknown error
203    Unknown,
204}
205
206/// Strategy performance tracking
207#[derive(Debug, Clone, Default)]
208pub struct StrategyPerformance {
209    /// Total number of times this strategy was attempted
210    pub total_attempts: u32,
211    /// Number of successful fallback attempts using this strategy
212    pub successful_attempts: u32,
213    /// Average quality score achieved by this strategy
214    pub average_quality: f32,
215    /// Average time taken by this strategy to execute
216    pub average_processing_time: Duration,
217    /// Recent success rate calculated from latest attempts
218    pub recent_success_rate: f32,
219    /// Overall effectiveness score combining success rate and quality
220    pub effectiveness_score: f32,
221}
222
223/// Performance tracking for the degradation controller
224#[derive(Debug, Default, Clone)]
225pub struct PerformanceTracker {
226    /// Total number of degradation attempts across all strategies
227    pub total_degradations: u64,
228    /// Number of degradation attempts that successfully recovered
229    pub successful_degradations: u64,
230    /// Average time spent executing fallback strategies
231    pub average_fallback_time: Duration,
232    /// Count of how many times each strategy has been used
233    pub strategy_usage: HashMap<String, u64>,
234    /// History of quality improvements achieved through fallbacks
235    pub quality_improvements: Vec<QualityImprovement>,
236}
237
238/// Quality improvement tracking
239#[derive(Debug, Clone)]
240pub struct QualityImprovement {
241    /// Quality score before applying the fallback strategy
242    pub original_quality: f32,
243    /// Quality score after applying the fallback strategy
244    pub final_quality: f32,
245    /// Name of the strategy that achieved the improvement
246    pub strategy_used: String,
247    /// Time taken to achieve the quality improvement
248    pub processing_time: Duration,
249    /// When this quality improvement was recorded
250    pub timestamp: Instant,
251}
252
253/// Failure history for learning and adaptation
254#[derive(Debug)]
255pub struct FailureHistory {
256    /// Recent failures by type
257    pub failures_by_type: HashMap<FailureType, Vec<FailureRecord>>,
258    /// Success patterns
259    pub success_patterns: Vec<SuccessPattern>,
260    /// Maximum history length
261    max_history_length: usize,
262}
263
264/// Individual failure record
265#[derive(Debug, Clone)]
266pub struct FailureRecord {
267    /// When this failure occurred
268    pub timestamp: Instant,
269    /// Type of failure that was encountered
270    pub failure_type: FailureType,
271    /// Type of conversion that was being attempted
272    pub conversion_type: ConversionType,
273    /// Additional context about the failure conditions
274    pub context: String,
275    /// Description of the resolution strategy if attempted
276    pub resolution: Option<String>,
277    /// Whether the resolution attempt was successful
278    pub resolution_success: bool,
279}
280
281/// Pattern of successful fallback resolution
282#[derive(Debug, Clone)]
283pub struct SuccessPattern {
284    /// Type of failure this pattern successfully handles
285    pub failure_type: FailureType,
286    /// Type of conversion this pattern applies to
287    pub conversion_type: ConversionType,
288    /// Name of the strategy that successfully resolved this pattern
289    pub successful_strategy: String,
290    /// Confidence score in this pattern's reliability (0.0 to 1.0)
291    pub confidence: f32,
292    /// Number of times this pattern has been successfully applied
293    pub usage_count: u32,
294}
295
296// Concrete fallback strategy implementations
297
298/// Passthrough fallback strategy - returns original audio
299#[derive(Debug)]
300pub struct PassthroughStrategy;
301
302impl FallbackStrategy for PassthroughStrategy {
303    fn name(&self) -> &str {
304        "passthrough"
305    }
306
307    fn can_handle(&self, _failure_type: &FailureType, _conversion_type: &ConversionType) -> bool {
308        true // Can always handle by doing nothing
309    }
310
311    fn success_probability(&self, _context: &FallbackContext) -> f32 {
312        1.0 // Always succeeds
313    }
314
315    fn apply(
316        &self,
317        request: &ConversionRequest,
318        _context: &FallbackContext,
319        _config: &ConversionConfig,
320    ) -> Result<ConversionResult> {
321        warn!("Applying passthrough fallback for request: {}", request.id);
322
323        // Return original audio unchanged
324        let processing_time = Duration::from_millis(1);
325        let mut result = ConversionResult::success(
326            request.id.clone(),
327            request.source_audio.clone(),
328            request.source_sample_rate,
329            processing_time,
330            request.conversion_type.clone(),
331        );
332
333        // Set quality metrics to indicate this is a fallback
334        result
335            .quality_metrics
336            .insert("overall_quality".to_string(), 0.5);
337        result
338            .quality_metrics
339            .insert("fallback_applied".to_string(), 1.0);
340        result
341            .quality_metrics
342            .insert("passthrough_strategy".to_string(), 1.0);
343
344        Ok(result)
345    }
346
347    fn priority(&self) -> i32 {
348        -100 // Lowest priority - last resort
349    }
350}
351
352/// Simplified processing strategy - uses basic algorithms
353#[derive(Debug)]
354pub struct SimplifiedProcessingStrategy;
355
356impl FallbackStrategy for SimplifiedProcessingStrategy {
357    fn name(&self) -> &str {
358        "simplified_processing"
359    }
360
361    fn can_handle(&self, failure_type: &FailureType, conversion_type: &ConversionType) -> bool {
362        match failure_type {
363            FailureType::QualityFailure
364            | FailureType::ProcessingError
365            | FailureType::PerformanceFailure => {
366                matches!(
367                    conversion_type,
368                    ConversionType::PitchShift | ConversionType::SpeedTransformation
369                )
370            }
371            _ => false,
372        }
373    }
374
375    fn success_probability(&self, context: &FallbackContext) -> f32 {
376        if context.available_resources.processing_capacity > 0.3 {
377            0.8
378        } else {
379            0.6
380        }
381    }
382
383    fn apply(
384        &self,
385        request: &ConversionRequest,
386        _context: &FallbackContext,
387        config: &ConversionConfig,
388    ) -> Result<ConversionResult> {
389        info!(
390            "Applying simplified processing fallback for request: {}",
391            request.id
392        );
393
394        let start_time = Instant::now();
395
396        // Use simple, robust algorithms
397        let converted_audio = match request.conversion_type {
398            ConversionType::PitchShift => {
399                let pitch_factor = 1.2; // Simple 20% pitch increase as fallback
400                let transform = PitchTransform::new(pitch_factor);
401                transform.apply(&request.source_audio)?
402            }
403            ConversionType::SpeedTransformation => {
404                let speed_factor = 1.1; // Simple 10% speed increase as fallback
405                let transform = SpeedTransform::new(speed_factor);
406                transform.apply(&request.source_audio)?
407            }
408            _ => request.source_audio.clone(), // Fallback to passthrough for unsupported types
409        };
410
411        let processing_time = start_time.elapsed();
412
413        let mut result = ConversionResult::success(
414            request.id.clone(),
415            converted_audio,
416            config.output_sample_rate,
417            processing_time,
418            request.conversion_type.clone(),
419        );
420
421        // Set quality metrics
422        result
423            .quality_metrics
424            .insert("overall_quality".to_string(), 0.6);
425        result
426            .quality_metrics
427            .insert("fallback_applied".to_string(), 1.0);
428        result
429            .quality_metrics
430            .insert("simplified_processing".to_string(), 1.0);
431
432        Ok(result)
433    }
434
435    fn priority(&self) -> i32 {
436        0 // Medium priority
437    }
438}
439
440// Implementation of GracefulDegradationController
441impl GracefulDegradationController {
442    /// Create new graceful degradation controller
443    pub fn new() -> Self {
444        Self::with_config(DegradationConfig::default())
445    }
446
447    /// Create with custom configuration
448    pub fn with_config(config: DegradationConfig) -> Self {
449        let quality_thresholds = QualityThresholds::default();
450        let strategy_executor = FallbackStrategyExecutor::new();
451        let performance_tracker = PerformanceTracker::default();
452        let failure_history = FailureHistory::new(1000);
453
454        Self {
455            config,
456            quality_thresholds,
457            strategy_executor,
458            performance_tracker,
459            failure_history,
460        }
461    }
462
463    /// Handle conversion failure with graceful degradation
464    pub async fn handle_failure(
465        &mut self,
466        request: &ConversionRequest,
467        original_error: Error,
468        config: &ConversionConfig,
469    ) -> Result<ConversionResult> {
470        let failure_type = self.classify_failure(&original_error);
471        let context = self
472            .build_fallback_context(&original_error, &failure_type, request)
473            .await;
474
475        info!(
476            "Handling failure type {:?} for request {}",
477            failure_type, request.id
478        );
479
480        // Record the failure for learning
481        self.record_failure(&failure_type, request, &original_error)
482            .await;
483
484        // Check if degradation is enabled
485        if !self.config.enable_degradation {
486            return Err(original_error);
487        }
488
489        // Attempt graceful degradation
490        self.attempt_graceful_degradation(request, &context, config)
491            .await
492    }
493
494    /// Handle quality-based degradation
495    pub async fn handle_quality_degradation(
496        &mut self,
497        request: &ConversionRequest,
498        result: &ConversionResult,
499        config: &ConversionConfig,
500    ) -> Result<Option<ConversionResult>> {
501        if !self.config.enable_quality_fallbacks {
502            return Ok(None);
503        }
504
505        // Check if quality is below acceptable thresholds
506        let overall_quality = result
507            .quality_metrics
508            .get("overall_quality")
509            .copied()
510            .unwrap_or(1.0);
511        let artifact_score = result
512            .artifacts
513            .as_ref()
514            .map(|a| a.overall_score)
515            .unwrap_or(0.0);
516
517        let needs_degradation = overall_quality < self.quality_thresholds.min_acceptable_quality
518            || artifact_score > self.quality_thresholds.max_artifact_score;
519
520        if !needs_degradation {
521            return Ok(None);
522        }
523
524        warn!(
525            "Quality degradation triggered: quality={:.3}, artifacts={:.3}",
526            overall_quality, artifact_score
527        );
528
529        let failure_type = if artifact_score > self.quality_thresholds.max_artifact_score {
530            FailureType::ArtifactFailure
531        } else {
532            FailureType::QualityFailure
533        };
534
535        let context = self
536            .build_quality_fallback_context(result, &failure_type)
537            .await;
538
539        match self
540            .attempt_graceful_degradation(request, &context, config)
541            .await
542        {
543            Ok(fallback_result) => Ok(Some(fallback_result)),
544            Err(_) => Ok(None), // Don't propagate fallback errors
545        }
546    }
547
548    /// Classify the type of failure
549    fn classify_failure(&self, error: &Error) -> FailureType {
550        match error {
551            Error::Processing { .. } => FailureType::ProcessingError,
552            Error::Model { .. } => FailureType::ModelFailure,
553            Error::Audio { .. } => FailureType::ProcessingError,
554            Error::Realtime { .. } => FailureType::PerformanceFailure,
555            Error::Streaming { .. } => FailureType::PerformanceFailure,
556            Error::Buffer { .. } => FailureType::MemoryFailure,
557            Error::Transform { .. } => FailureType::ProcessingError,
558            Error::Validation { .. } => FailureType::ProcessingError,
559            Error::Runtime { .. } => FailureType::Unknown,
560            Error::Io(_) => FailureType::ResourceFailure,
561            Error::Candle(_) => FailureType::ModelFailure,
562            _ => FailureType::Unknown,
563        }
564    }
565
566    /// Build fallback context from error and request
567    async fn build_fallback_context(
568        &self,
569        error: &Error,
570        failure_type: &FailureType,
571        request: &ConversionRequest,
572    ) -> FallbackContext {
573        let resource_context = self.assess_current_resources().await;
574        let time_constraints = self.build_time_constraints(request).await;
575
576        FallbackContext {
577            original_error: Some(error.to_string()),
578            current_quality: None,
579            artifacts: None,
580            previous_attempts: Vec::new(),
581            available_resources: resource_context,
582            time_constraints,
583        }
584    }
585
586    /// Build context for quality-based fallback
587    async fn build_quality_fallback_context(
588        &self,
589        result: &ConversionResult,
590        failure_type: &FailureType,
591    ) -> FallbackContext {
592        let resource_context = self.assess_current_resources().await;
593        let overall_quality = result.quality_metrics.get("overall_quality").copied();
594
595        FallbackContext {
596            original_error: Some(format!("Quality below threshold: {overall_quality:?}")),
597            current_quality: overall_quality,
598            artifacts: result.artifacts.clone(),
599            previous_attempts: Vec::new(),
600            available_resources: resource_context,
601            time_constraints: TimeConstraints {
602                max_processing_time: Duration::from_millis(self.config.max_recovery_time_ms),
603                deadline: None,
604                real_time_requirement: false,
605            },
606        }
607    }
608
609    /// Attempt graceful degradation using available strategies
610    async fn attempt_graceful_degradation(
611        &mut self,
612        request: &ConversionRequest,
613        context: &FallbackContext,
614        config: &ConversionConfig,
615    ) -> Result<ConversionResult> {
616        let start_time = Instant::now();
617        let mut current_context = context.clone();
618        let mut attempt_count = 0;
619
620        while attempt_count < self.config.max_retry_attempts {
621            if start_time.elapsed() > Duration::from_millis(self.config.max_recovery_time_ms) {
622                warn!("Fallback timeout exceeded, using final strategy");
623                break;
624            }
625
626            // Find best strategy for current situation
627            if let Some(strategy) = self
628                .strategy_executor
629                .select_best_strategy(&current_context, &request.conversion_type)
630            {
631                let attempt_start = Instant::now();
632                let strategy_name = strategy.name().to_string(); // Clone the name to avoid borrowing issues
633
634                info!(
635                    "Attempting fallback strategy '{}' (attempt {}/{})",
636                    strategy_name,
637                    attempt_count + 1,
638                    self.config.max_retry_attempts
639                );
640
641                match strategy.apply(request, &current_context, config) {
642                    Ok(result) => {
643                        let processing_time = attempt_start.elapsed();
644
645                        // Record successful attempt
646                        self.record_successful_fallback(&strategy_name, &result, processing_time)
647                            .await;
648
649                        // Validate result quality
650                        if self.validate_fallback_result(&result) {
651                            info!("Fallback strategy '{}' succeeded", strategy_name);
652                            self.performance_tracker.successful_degradations += 1;
653                            return Ok(result);
654                        } else {
655                            warn!(
656                                "Fallback strategy '{}' produced poor quality",
657                                strategy_name
658                            );
659                        }
660                    }
661                    Err(e) => {
662                        warn!("Fallback strategy '{}' failed: {}", strategy_name, e);
663
664                        // Record failed attempt
665                        let failed_attempt = FallbackAttempt {
666                            strategy_name: strategy_name.clone(),
667                            success: false,
668                            quality_achieved: None,
669                            processing_time: attempt_start.elapsed(),
670                            error: Some(e.to_string()),
671                        };
672                        current_context.previous_attempts.push(failed_attempt);
673                    }
674                }
675            }
676
677            attempt_count += 1;
678        }
679
680        // Final fallback - use passthrough strategy
681        warn!("All fallback strategies failed, applying final passthrough");
682        let passthrough = PassthroughStrategy;
683        passthrough.apply(request, &current_context, config)
684    }
685
686    /// Validate fallback result quality
687    fn validate_fallback_result(&self, result: &ConversionResult) -> bool {
688        // Basic validation - result should be successful and have audio
689        if !result.success || result.converted_audio.is_empty() {
690            return false;
691        }
692
693        // Check for reasonable quality metrics
694        if let Some(quality) = result.quality_metrics.get("overall_quality") {
695            if *quality < 0.1 {
696                return false;
697            }
698        }
699
700        true
701    }
702
703    /// Assess current system resources
704    async fn assess_current_resources(&self) -> ResourceContext {
705        // In a real implementation, this would query actual system resources
706        // For now, provide reasonable defaults
707        ResourceContext {
708            cpu_usage_percent: 50.0,
709            memory_available_mb: 1024.0,
710            gpu_available: true,
711            processing_capacity: 0.7,
712        }
713    }
714
715    /// Build time constraints for processing
716    async fn build_time_constraints(&self, request: &ConversionRequest) -> TimeConstraints {
717        let is_realtime = request.realtime;
718        let max_time = if is_realtime {
719            Duration::from_millis(100) // Strict for real-time
720        } else {
721            Duration::from_millis(self.config.max_recovery_time_ms)
722        };
723
724        TimeConstraints {
725            max_processing_time: max_time,
726            deadline: None,
727            real_time_requirement: is_realtime,
728        }
729    }
730
731    /// Record failure for learning and analysis
732    async fn record_failure(
733        &mut self,
734        failure_type: &FailureType,
735        request: &ConversionRequest,
736        error: &Error,
737    ) {
738        let failure_record = FailureRecord {
739            timestamp: Instant::now(),
740            failure_type: failure_type.clone(),
741            conversion_type: request.conversion_type.clone(),
742            context: format!("Request: {}, Error: {}", request.id, error),
743            resolution: None,
744            resolution_success: false,
745        };
746
747        self.failure_history.record_failure(failure_record);
748        self.performance_tracker.total_degradations += 1;
749    }
750
751    /// Record successful fallback attempt
752    async fn record_successful_fallback(
753        &mut self,
754        strategy_name: &str,
755        result: &ConversionResult,
756        processing_time: Duration,
757    ) {
758        // Update strategy performance
759        self.strategy_executor
760            .record_success(strategy_name, result, processing_time);
761
762        // Update overall performance tracking
763        if let Some(quality) = result.quality_metrics.get("overall_quality") {
764            let improvement = QualityImprovement {
765                original_quality: 0.0, // Would track from original failure
766                final_quality: *quality,
767                strategy_used: strategy_name.to_string(),
768                processing_time,
769                timestamp: Instant::now(),
770            };
771            self.performance_tracker
772                .quality_improvements
773                .push(improvement);
774        }
775
776        // Update strategy usage count
777        *self
778            .performance_tracker
779            .strategy_usage
780            .entry(strategy_name.to_string())
781            .or_insert(0) += 1;
782    }
783
784    /// Get performance statistics
785    pub fn get_performance_stats(&self) -> &PerformanceTracker {
786        &self.performance_tracker
787    }
788
789    /// Update quality thresholds
790    pub fn update_quality_thresholds(&mut self, thresholds: QualityThresholds) {
791        self.quality_thresholds = thresholds;
792    }
793
794    /// Get current quality thresholds
795    pub fn get_quality_thresholds(&self) -> &QualityThresholds {
796        &self.quality_thresholds
797    }
798
799    /// Enable or disable specific degradation features
800    pub fn configure(&mut self, config: DegradationConfig) {
801        self.config = config;
802    }
803}
804
805impl Default for FallbackStrategyExecutor {
806    fn default() -> Self {
807        Self::new()
808    }
809}
810
811// Implementation of FallbackStrategyExecutor
812impl FallbackStrategyExecutor {
813    /// Create new strategy executor with default strategies
814    pub fn new() -> Self {
815        let strategies: Vec<Box<dyn FallbackStrategy>> = vec![
816            Box::new(SimplifiedProcessingStrategy),
817            Box::new(PassthroughStrategy),
818            Box::new(QualityAdjustmentStrategy),
819            Box::new(ResourceOptimizationStrategy),
820            Box::new(AlternativeAlgorithmStrategy),
821        ];
822
823        Self {
824            strategies,
825            strategy_performance: HashMap::new(),
826        }
827    }
828
829    /// Select the best strategy for the given context
830    pub fn select_best_strategy(
831        &self,
832        context: &FallbackContext,
833        conversion_type: &ConversionType,
834    ) -> Option<&dyn FallbackStrategy> {
835        let failure_type = if let Some(ref error) = context.original_error {
836            if error.contains("quality") {
837                FailureType::QualityFailure
838            } else if error.contains("timeout") {
839                FailureType::PerformanceFailure
840            } else if error.contains("memory") {
841                FailureType::MemoryFailure
842            } else {
843                FailureType::ProcessingError
844            }
845        } else {
846            FailureType::Unknown
847        };
848
849        // Filter strategies that can handle this failure type
850        let mut candidates: Vec<&dyn FallbackStrategy> = self
851            .strategies
852            .iter()
853            .map(|s| s.as_ref())
854            .filter(|s| s.can_handle(&failure_type, conversion_type))
855            .collect();
856
857        if candidates.is_empty() {
858            return None;
859        }
860
861        // Sort by effectiveness (combination of priority and success probability)
862        candidates.sort_by(|a, b| {
863            let score_a = (a.priority() as f32) + a.success_probability(context);
864            let score_b = (b.priority() as f32) + b.success_probability(context);
865            score_b
866                .partial_cmp(&score_a)
867                .unwrap_or(std::cmp::Ordering::Equal)
868        });
869
870        candidates.into_iter().next()
871    }
872
873    /// Record successful strategy execution
874    pub fn record_success(
875        &mut self,
876        strategy_name: &str,
877        result: &ConversionResult,
878        processing_time: Duration,
879    ) {
880        let perf = self
881            .strategy_performance
882            .entry(strategy_name.to_string())
883            .or_default();
884
885        perf.total_attempts += 1;
886        perf.successful_attempts += 1;
887
888        if let Some(quality) = result.quality_metrics.get("overall_quality") {
889            perf.average_quality = (perf.average_quality * (perf.successful_attempts - 1) as f32
890                + quality)
891                / perf.successful_attempts as f32;
892        }
893
894        perf.average_processing_time = Duration::from_nanos(
895            ((perf.average_processing_time.as_nanos() * (perf.successful_attempts - 1) as u128
896                + processing_time.as_nanos())
897                / perf.successful_attempts as u128) as u64,
898        );
899
900        // Update recent success rate (simplified)
901        perf.recent_success_rate = perf.successful_attempts as f32 / perf.total_attempts as f32;
902        perf.effectiveness_score = perf.recent_success_rate * perf.average_quality;
903    }
904
905    /// Add custom fallback strategy
906    pub fn add_strategy(&mut self, strategy: Box<dyn FallbackStrategy>) {
907        self.strategies.push(strategy);
908    }
909
910    /// Get strategy performance statistics
911    pub fn get_strategy_stats(&self) -> &HashMap<String, StrategyPerformance> {
912        &self.strategy_performance
913    }
914}
915
916// Implementation of FailureHistory
917impl FailureHistory {
918    /// Create new failure history tracker
919    pub fn new(max_history_length: usize) -> Self {
920        Self {
921            failures_by_type: HashMap::new(),
922            success_patterns: Vec::new(),
923            max_history_length,
924        }
925    }
926
927    /// Record a failure
928    pub fn record_failure(&mut self, failure: FailureRecord) {
929        let failures = self
930            .failures_by_type
931            .entry(failure.failure_type.clone())
932            .or_default();
933
934        failures.push(failure);
935
936        // Limit history size
937        if failures.len() > self.max_history_length {
938            failures.remove(0);
939        }
940    }
941
942    /// Update success pattern
943    pub fn record_success_pattern(
944        &mut self,
945        failure_type: FailureType,
946        conversion_type: ConversionType,
947        successful_strategy: String,
948    ) {
949        // Look for existing pattern
950        if let Some(pattern) = self.success_patterns.iter_mut().find(|p| {
951            p.failure_type == failure_type
952                && p.conversion_type == conversion_type
953                && p.successful_strategy == successful_strategy
954        }) {
955            pattern.usage_count += 1;
956            pattern.confidence = (pattern.confidence * 0.9 + 0.1).min(1.0);
957        } else {
958            // Add new pattern
959            self.success_patterns.push(SuccessPattern {
960                failure_type,
961                conversion_type,
962                successful_strategy,
963                confidence: 0.5,
964                usage_count: 1,
965            });
966        }
967
968        // Limit pattern history
969        if self.success_patterns.len() > self.max_history_length / 2 {
970            self.success_patterns
971                .sort_by(|a, b| a.usage_count.cmp(&b.usage_count));
972            self.success_patterns.remove(0);
973        }
974    }
975
976    /// Get failure statistics
977    pub fn get_failure_stats(&self) -> HashMap<FailureType, usize> {
978        self.failures_by_type
979            .iter()
980            .map(|(k, v)| (k.clone(), v.len()))
981            .collect()
982    }
983
984    /// Get most successful strategy for a failure type
985    pub fn get_best_strategy(
986        &self,
987        failure_type: &FailureType,
988        conversion_type: &ConversionType,
989    ) -> Option<String> {
990        self.success_patterns
991            .iter()
992            .filter(|p| &p.failure_type == failure_type && &p.conversion_type == conversion_type)
993            .max_by(|a, b| {
994                let score_a = a.confidence * a.usage_count as f32;
995                let score_b = b.confidence * b.usage_count as f32;
996                score_a
997                    .partial_cmp(&score_b)
998                    .unwrap_or(std::cmp::Ordering::Equal)
999            })
1000            .map(|p| p.successful_strategy.clone())
1001    }
1002}
1003
1004/// Quality Adjustment Strategy - Adjusts quality parameters to improve results
1005#[derive(Debug)]
1006pub struct QualityAdjustmentStrategy;
1007
1008impl FallbackStrategy for QualityAdjustmentStrategy {
1009    fn name(&self) -> &str {
1010        "quality_adjustment"
1011    }
1012
1013    fn can_handle(&self, failure_type: &FailureType, conversion_type: &ConversionType) -> bool {
1014        match failure_type {
1015            FailureType::QualityFailure | FailureType::ArtifactFailure => {
1016                matches!(
1017                    conversion_type,
1018                    ConversionType::SpeakerConversion
1019                        | ConversionType::PitchShift
1020                        | ConversionType::SpeedTransformation
1021                        | ConversionType::EmotionalTransformation
1022                )
1023            }
1024            _ => false,
1025        }
1026    }
1027
1028    fn success_probability(&self, context: &FallbackContext) -> f32 {
1029        // Higher probability if we have quality metrics and moderate artifact levels
1030        let quality_factor = context.current_quality.unwrap_or(0.5);
1031        let artifact_factor = context
1032            .artifacts
1033            .as_ref()
1034            .map(|a| 1.0 - a.overall_score)
1035            .unwrap_or(0.5);
1036
1037        // Base probability adjusted by quality and artifact metrics
1038        let base_probability = 0.7;
1039        base_probability * quality_factor * artifact_factor
1040    }
1041
1042    fn apply(
1043        &self,
1044        request: &ConversionRequest,
1045        context: &FallbackContext,
1046        config: &ConversionConfig,
1047    ) -> Result<ConversionResult> {
1048        debug!("Applying quality adjustment strategy");
1049
1050        // Create adjusted configuration with more conservative settings
1051        let mut adjusted_config = config.clone();
1052
1053        // Reduce quality level to be more conservative
1054        adjusted_config.quality_level = (adjusted_config.quality_level * 0.8).max(0.1);
1055
1056        // Apply more conservative processing parameters
1057        let input_audio = &request.source_audio;
1058        let mut output_audio = input_audio.clone();
1059
1060        // Apply gentle processing based on conversion type
1061        match &request.conversion_type {
1062            ConversionType::PitchShift => {
1063                // Apply conservative pitch shifting (1.1x factor)
1064                let pitch_transform = PitchTransform::new(1.1);
1065                output_audio = pitch_transform.apply(&output_audio)?;
1066            }
1067            ConversionType::SpeedTransformation => {
1068                // Apply conservative speed transformation (0.95x factor)
1069                let speed_transform = SpeedTransform::new(0.95);
1070                output_audio = speed_transform.apply(&output_audio)?;
1071            }
1072            _ => {
1073                // For other types, apply minimal processing
1074                output_audio = input_audio.clone();
1075            }
1076        }
1077
1078        Ok(ConversionResult {
1079            request_id: request.id.clone(),
1080            converted_audio: output_audio,
1081            output_sample_rate: request.source_sample_rate,
1082            quality_metrics: HashMap::new(),
1083            artifacts: None,
1084            objective_quality: Some(ObjectiveQualityMetrics {
1085                overall_score: 0.7,
1086                spectral_similarity: 0.7,
1087                temporal_consistency: 0.8,
1088                prosodic_preservation: 0.7,
1089                naturalness: 0.7,
1090                perceptual_quality: 0.7,
1091                snr_estimate: 20.0,
1092                segmental_snr: 18.0,
1093            }),
1094            processing_time: context
1095                .time_constraints
1096                .max_processing_time
1097                .min(Duration::from_millis(100)),
1098            conversion_type: request.conversion_type.clone(),
1099            success: true,
1100            error_message: None,
1101            timestamp: SystemTime::now(),
1102        })
1103    }
1104
1105    fn priority(&self) -> i32 {
1106        75 // High priority for quality issues
1107    }
1108}
1109
1110/// Resource Optimization Strategy - Optimizes resource usage when resources are constrained
1111#[derive(Debug)]
1112pub struct ResourceOptimizationStrategy;
1113
1114impl FallbackStrategy for ResourceOptimizationStrategy {
1115    fn name(&self) -> &str {
1116        "resource_optimization"
1117    }
1118
1119    fn can_handle(&self, failure_type: &FailureType, _conversion_type: &ConversionType) -> bool {
1120        matches!(
1121            failure_type,
1122            FailureType::ResourceFailure
1123                | FailureType::MemoryFailure
1124                | FailureType::PerformanceFailure
1125        )
1126    }
1127
1128    fn success_probability(&self, context: &FallbackContext) -> f32 {
1129        // Higher probability if we have resource constraints
1130        let cpu_factor = 1.0 - (context.available_resources.cpu_usage_percent / 100.0);
1131        let memory_factor = if context.available_resources.memory_available_mb > 1000.0 {
1132            0.8
1133        } else {
1134            0.3
1135        };
1136
1137        // Base probability adjusted by available resources
1138        let base_probability = 0.8_f32;
1139        base_probability * ((cpu_factor + memory_factor) / 2.0).max(0.2)
1140    }
1141
1142    fn apply(
1143        &self,
1144        request: &ConversionRequest,
1145        context: &FallbackContext,
1146        config: &ConversionConfig,
1147    ) -> Result<ConversionResult> {
1148        debug!("Applying resource optimization strategy");
1149
1150        // Create optimized configuration for low resource usage
1151        let mut optimized_config = config.clone();
1152
1153        // Reduce buffer sizes and processing complexity
1154        optimized_config.buffer_size = optimized_config.buffer_size.min(1024);
1155        // Note: enable_gpu_acceleration not available in this config, using CPU-optimized settings
1156
1157        // Reduce processing quality for speed
1158        optimized_config.quality_level = optimized_config.quality_level.min(0.5);
1159
1160        let input_audio = &request.source_audio;
1161        let mut output_audio = input_audio.clone();
1162
1163        // Apply minimal processing to conserve resources
1164        match &request.conversion_type {
1165            ConversionType::PitchShift => {
1166                // Use simple pitch shifting for minimal resource usage
1167                let pitch_transform = PitchTransform::new(1.05);
1168                output_audio = pitch_transform.apply(&output_audio)?;
1169            }
1170            ConversionType::SpeedTransformation => {
1171                let speed_transform = SpeedTransform::new(0.98);
1172                output_audio = speed_transform.apply(&output_audio)?;
1173            }
1174            _ => {
1175                // For complex conversions, use simplified processing
1176                output_audio = input_audio.clone();
1177            }
1178        }
1179
1180        Ok(ConversionResult {
1181            request_id: request.id.clone(),
1182            converted_audio: output_audio,
1183            output_sample_rate: request.source_sample_rate,
1184            quality_metrics: HashMap::new(),
1185            artifacts: None,
1186            objective_quality: Some(ObjectiveQualityMetrics {
1187                overall_score: 0.6,
1188                spectral_similarity: 0.6,
1189                temporal_consistency: 0.7,
1190                prosodic_preservation: 0.6,
1191                naturalness: 0.6,
1192                perceptual_quality: 0.6,
1193                snr_estimate: 18.0,
1194                segmental_snr: 16.0,
1195            }),
1196            processing_time: Duration::from_millis(50),
1197            conversion_type: request.conversion_type.clone(),
1198            success: true,
1199            error_message: None,
1200            timestamp: SystemTime::now(),
1201        })
1202    }
1203
1204    fn priority(&self) -> i32 {
1205        60 // Medium-high priority for resource issues
1206    }
1207}
1208
1209/// Alternative Algorithm Strategy - Uses alternative algorithms when primary ones fail
1210#[derive(Debug)]
1211pub struct AlternativeAlgorithmStrategy;
1212
1213impl FallbackStrategy for AlternativeAlgorithmStrategy {
1214    fn name(&self) -> &str {
1215        "alternative_algorithm"
1216    }
1217
1218    fn can_handle(&self, failure_type: &FailureType, conversion_type: &ConversionType) -> bool {
1219        match failure_type {
1220            FailureType::ProcessingError | FailureType::ModelFailure => {
1221                matches!(
1222                    conversion_type,
1223                    ConversionType::SpeakerConversion
1224                        | ConversionType::PitchShift
1225                        | ConversionType::SpeedTransformation
1226                        | ConversionType::AgeTransformation
1227                        | ConversionType::GenderTransformation
1228                )
1229            }
1230            _ => false,
1231        }
1232    }
1233
1234    fn success_probability(&self, context: &FallbackContext) -> f32 {
1235        // Higher probability if previous attempts have failed
1236        let attempt_factor = if context.previous_attempts.len() > 1 {
1237            0.8
1238        } else {
1239            0.6
1240        };
1241
1242        // Consider available processing time
1243        let time_factor = if context.time_constraints.max_processing_time.as_millis() > 1000 {
1244            1.0
1245        } else {
1246            0.7
1247        };
1248
1249        attempt_factor * time_factor
1250    }
1251
1252    fn apply(
1253        &self,
1254        request: &ConversionRequest,
1255        context: &FallbackContext,
1256        _config: &ConversionConfig,
1257    ) -> Result<ConversionResult> {
1258        debug!("Applying alternative algorithm strategy");
1259
1260        let input_audio = &request.source_audio;
1261        let mut output_audio = input_audio.clone();
1262
1263        // Use alternative, more robust algorithms
1264        match &request.conversion_type {
1265            ConversionType::PitchShift => {
1266                // Use basic time-domain pitch shifting instead of complex frequency-domain methods
1267                let factor = 1.2; // Fixed factor for alternative approach
1268                let samples_per_frame = 512;
1269
1270                for chunk in output_audio.chunks_mut(samples_per_frame) {
1271                    if chunk.len() == samples_per_frame {
1272                        // Simple pitch shifting by resampling
1273                        let mut resampled = Vec::with_capacity(chunk.len());
1274                        for i in 0..chunk.len() {
1275                            let src_idx = ((i as f32) / factor) as usize;
1276                            if src_idx < chunk.len() {
1277                                resampled.push(chunk[src_idx]);
1278                            } else {
1279                                resampled.push(0.0);
1280                            }
1281                        }
1282                        chunk.copy_from_slice(&resampled);
1283                    }
1284                }
1285            }
1286            ConversionType::SpeedTransformation => {
1287                // Use simple time-stretching without pitch preservation
1288                let speed_factor = 0.9; // Fixed factor for alternative approach
1289                let target_len = ((input_audio.len() as f32) / speed_factor) as usize;
1290                let mut stretched = Vec::with_capacity(target_len);
1291
1292                for i in 0..target_len {
1293                    let src_idx = ((i as f32) * speed_factor) as usize;
1294                    if src_idx < input_audio.len() {
1295                        stretched.push(input_audio[src_idx]);
1296                    } else {
1297                        stretched.push(0.0);
1298                    }
1299                }
1300                output_audio = stretched;
1301            }
1302            ConversionType::AgeTransformation => {
1303                // Use simple formant shifting approximation
1304                for sample in output_audio.iter_mut() {
1305                    *sample *= 0.9; // Simple age simulation through amplitude reduction
1306                }
1307            }
1308            ConversionType::GenderTransformation => {
1309                // Use basic spectral modification
1310                for (i, sample) in output_audio.iter_mut().enumerate() {
1311                    if i % 2 == 0 {
1312                        *sample *= 1.1; // Simple formant approximation
1313                    }
1314                }
1315            }
1316            _ => {
1317                // For other types, apply passthrough with minimal processing
1318                output_audio = input_audio.clone();
1319            }
1320        }
1321
1322        Ok(ConversionResult {
1323            request_id: request.id.clone(),
1324            converted_audio: output_audio,
1325            output_sample_rate: request.source_sample_rate,
1326            quality_metrics: HashMap::new(),
1327            artifacts: None,
1328            objective_quality: Some(ObjectiveQualityMetrics {
1329                overall_score: 0.65,
1330                spectral_similarity: 0.65,
1331                temporal_consistency: 0.7,
1332                prosodic_preservation: 0.6,
1333                naturalness: 0.65,
1334                perceptual_quality: 0.65,
1335                snr_estimate: 19.0,
1336                segmental_snr: 17.0,
1337            }),
1338            processing_time: context
1339                .time_constraints
1340                .max_processing_time
1341                .min(Duration::from_millis(200)),
1342            conversion_type: request.conversion_type.clone(),
1343            success: true,
1344            error_message: None,
1345            timestamp: SystemTime::now(),
1346        })
1347    }
1348
1349    fn priority(&self) -> i32 {
1350        50 // Medium priority - try after quality adjustment but before passthrough
1351    }
1352}
1353
1354/// Default implementation
1355impl Default for GracefulDegradationController {
1356    fn default() -> Self {
1357        Self::new()
1358    }
1359}