1use 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#[derive(Debug)]
24pub struct GracefulDegradationController {
25 config: DegradationConfig,
27 quality_thresholds: QualityThresholds,
29 strategy_executor: FallbackStrategyExecutor,
31 performance_tracker: PerformanceTracker,
33 failure_history: FailureHistory,
35}
36
37#[derive(Debug, Clone)]
39pub struct DegradationConfig {
40 pub enable_degradation: bool,
42 pub enable_quality_fallbacks: bool,
44 pub enable_error_recovery: bool,
46 pub max_retry_attempts: u32,
48 pub max_recovery_time_ms: u64,
50 pub enable_learning: bool,
52 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#[derive(Debug, Clone)]
72pub struct QualityThresholds {
73 pub min_acceptable_quality: f32,
75 pub complexity_reduction_threshold: f32,
77 pub simple_algorithm_threshold: f32,
79 pub passthrough_threshold: f32,
81 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#[derive(Debug)]
99pub struct FallbackStrategyExecutor {
100 strategies: Vec<Box<dyn FallbackStrategy>>,
102 strategy_performance: HashMap<String, StrategyPerformance>,
104}
105
106pub trait FallbackStrategy: Send + Sync + std::fmt::Debug {
108 fn name(&self) -> &str;
110
111 fn can_handle(&self, failure_type: &FailureType, conversion_type: &ConversionType) -> bool;
113
114 fn success_probability(&self, context: &FallbackContext) -> f32;
116
117 fn apply(
119 &self,
120 request: &ConversionRequest,
121 context: &FallbackContext,
122 config: &ConversionConfig,
123 ) -> Result<ConversionResult>;
124
125 fn priority(&self) -> i32;
127}
128
129#[derive(Debug, Clone)]
131pub struct FallbackContext {
132 pub original_error: Option<String>,
134 pub current_quality: Option<f32>,
136 pub artifacts: Option<DetectedArtifacts>,
138 pub previous_attempts: Vec<FallbackAttempt>,
140 pub available_resources: ResourceContext,
142 pub time_constraints: TimeConstraints,
144}
145
146#[derive(Debug, Clone)]
148pub struct FallbackAttempt {
149 pub strategy_name: String,
151 pub success: bool,
153 pub quality_achieved: Option<f32>,
155 pub processing_time: Duration,
157 pub error: Option<String>,
159}
160
161#[derive(Debug, Clone)]
163pub struct ResourceContext {
164 pub cpu_usage_percent: f32,
166 pub memory_available_mb: f64,
168 pub gpu_available: bool,
170 pub processing_capacity: f32, }
173
174#[derive(Debug, Clone)]
176pub struct TimeConstraints {
177 pub max_processing_time: Duration,
179 pub deadline: Option<Instant>,
181 pub real_time_requirement: bool,
183}
184
185#[derive(Debug, Clone, PartialEq, Eq, Hash)]
187pub enum FailureType {
188 ProcessingError,
190 QualityFailure,
192 ArtifactFailure,
194 PerformanceFailure,
196 ResourceFailure,
198 ModelFailure,
200 MemoryFailure,
202 Unknown,
204}
205
206#[derive(Debug, Clone, Default)]
208pub struct StrategyPerformance {
209 pub total_attempts: u32,
211 pub successful_attempts: u32,
213 pub average_quality: f32,
215 pub average_processing_time: Duration,
217 pub recent_success_rate: f32,
219 pub effectiveness_score: f32,
221}
222
223#[derive(Debug, Default, Clone)]
225pub struct PerformanceTracker {
226 pub total_degradations: u64,
228 pub successful_degradations: u64,
230 pub average_fallback_time: Duration,
232 pub strategy_usage: HashMap<String, u64>,
234 pub quality_improvements: Vec<QualityImprovement>,
236}
237
238#[derive(Debug, Clone)]
240pub struct QualityImprovement {
241 pub original_quality: f32,
243 pub final_quality: f32,
245 pub strategy_used: String,
247 pub processing_time: Duration,
249 pub timestamp: Instant,
251}
252
253#[derive(Debug)]
255pub struct FailureHistory {
256 pub failures_by_type: HashMap<FailureType, Vec<FailureRecord>>,
258 pub success_patterns: Vec<SuccessPattern>,
260 max_history_length: usize,
262}
263
264#[derive(Debug, Clone)]
266pub struct FailureRecord {
267 pub timestamp: Instant,
269 pub failure_type: FailureType,
271 pub conversion_type: ConversionType,
273 pub context: String,
275 pub resolution: Option<String>,
277 pub resolution_success: bool,
279}
280
281#[derive(Debug, Clone)]
283pub struct SuccessPattern {
284 pub failure_type: FailureType,
286 pub conversion_type: ConversionType,
288 pub successful_strategy: String,
290 pub confidence: f32,
292 pub usage_count: u32,
294}
295
296#[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 }
310
311 fn success_probability(&self, _context: &FallbackContext) -> f32 {
312 1.0 }
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 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 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 }
350}
351
352#[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 let converted_audio = match request.conversion_type {
398 ConversionType::PitchShift => {
399 let pitch_factor = 1.2; let transform = PitchTransform::new(pitch_factor);
401 transform.apply(&request.source_audio)?
402 }
403 ConversionType::SpeedTransformation => {
404 let speed_factor = 1.1; let transform = SpeedTransform::new(speed_factor);
406 transform.apply(&request.source_audio)?
407 }
408 _ => request.source_audio.clone(), };
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 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 }
438}
439
440impl GracefulDegradationController {
442 pub fn new() -> Self {
444 Self::with_config(DegradationConfig::default())
445 }
446
447 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 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 self.record_failure(&failure_type, request, &original_error)
482 .await;
483
484 if !self.config.enable_degradation {
486 return Err(original_error);
487 }
488
489 self.attempt_graceful_degradation(request, &context, config)
491 .await
492 }
493
494 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 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), }
546 }
547
548 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 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 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 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 if let Some(strategy) = self
628 .strategy_executor
629 .select_best_strategy(¤t_context, &request.conversion_type)
630 {
631 let attempt_start = Instant::now();
632 let strategy_name = strategy.name().to_string(); 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, ¤t_context, config) {
642 Ok(result) => {
643 let processing_time = attempt_start.elapsed();
644
645 self.record_successful_fallback(&strategy_name, &result, processing_time)
647 .await;
648
649 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 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 warn!("All fallback strategies failed, applying final passthrough");
682 let passthrough = PassthroughStrategy;
683 passthrough.apply(request, ¤t_context, config)
684 }
685
686 fn validate_fallback_result(&self, result: &ConversionResult) -> bool {
688 if !result.success || result.converted_audio.is_empty() {
690 return false;
691 }
692
693 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 async fn assess_current_resources(&self) -> ResourceContext {
705 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 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) } 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 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 async fn record_successful_fallback(
753 &mut self,
754 strategy_name: &str,
755 result: &ConversionResult,
756 processing_time: Duration,
757 ) {
758 self.strategy_executor
760 .record_success(strategy_name, result, processing_time);
761
762 if let Some(quality) = result.quality_metrics.get("overall_quality") {
764 let improvement = QualityImprovement {
765 original_quality: 0.0, 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 *self
778 .performance_tracker
779 .strategy_usage
780 .entry(strategy_name.to_string())
781 .or_insert(0) += 1;
782 }
783
784 pub fn get_performance_stats(&self) -> &PerformanceTracker {
786 &self.performance_tracker
787 }
788
789 pub fn update_quality_thresholds(&mut self, thresholds: QualityThresholds) {
791 self.quality_thresholds = thresholds;
792 }
793
794 pub fn get_quality_thresholds(&self) -> &QualityThresholds {
796 &self.quality_thresholds
797 }
798
799 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
811impl FallbackStrategyExecutor {
813 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 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 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 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 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 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 pub fn add_strategy(&mut self, strategy: Box<dyn FallbackStrategy>) {
907 self.strategies.push(strategy);
908 }
909
910 pub fn get_strategy_stats(&self) -> &HashMap<String, StrategyPerformance> {
912 &self.strategy_performance
913 }
914}
915
916impl FailureHistory {
918 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 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 if failures.len() > self.max_history_length {
938 failures.remove(0);
939 }
940 }
941
942 pub fn record_success_pattern(
944 &mut self,
945 failure_type: FailureType,
946 conversion_type: ConversionType,
947 successful_strategy: String,
948 ) {
949 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 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 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 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 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#[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 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 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 let mut adjusted_config = config.clone();
1052
1053 adjusted_config.quality_level = (adjusted_config.quality_level * 0.8).max(0.1);
1055
1056 let input_audio = &request.source_audio;
1058 let mut output_audio = input_audio.clone();
1059
1060 match &request.conversion_type {
1062 ConversionType::PitchShift => {
1063 let pitch_transform = PitchTransform::new(1.1);
1065 output_audio = pitch_transform.apply(&output_audio)?;
1066 }
1067 ConversionType::SpeedTransformation => {
1068 let speed_transform = SpeedTransform::new(0.95);
1070 output_audio = speed_transform.apply(&output_audio)?;
1071 }
1072 _ => {
1073 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 }
1108}
1109
1110#[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 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 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 let mut optimized_config = config.clone();
1152
1153 optimized_config.buffer_size = optimized_config.buffer_size.min(1024);
1155 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 match &request.conversion_type {
1165 ConversionType::PitchShift => {
1166 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 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 }
1207}
1208
1209#[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 let attempt_factor = if context.previous_attempts.len() > 1 {
1237 0.8
1238 } else {
1239 0.6
1240 };
1241
1242 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 match &request.conversion_type {
1265 ConversionType::PitchShift => {
1266 let factor = 1.2; 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 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 let speed_factor = 0.9; 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 for sample in output_audio.iter_mut() {
1305 *sample *= 0.9; }
1307 }
1308 ConversionType::GenderTransformation => {
1309 for (i, sample) in output_audio.iter_mut().enumerate() {
1311 if i % 2 == 0 {
1312 *sample *= 1.1; }
1314 }
1315 }
1316 _ => {
1317 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 }
1352}
1353
1354impl Default for GracefulDegradationController {
1356 fn default() -> Self {
1357 Self::new()
1358 }
1359}