use serde::{Deserialize, Serialize};
use std::collections::{BTreeMap, HashMap, HashSet};
use std::hash::{Hash, Hasher};
use std::sync::Arc;
use thiserror::Error;
pub use self::advanced::*;
pub use self::config::*;
pub use self::intonation::*;
pub use self::phonological::*;
pub use self::results::*;
pub use self::rhythm::*;
pub use self::stress::*;
pub use self::timing::*;
pub mod advanced;
pub mod config;
pub mod intonation;
pub mod phonological;
pub mod results;
pub mod rhythm;
pub mod stress;
pub mod timing;
#[derive(Debug, Clone)]
pub struct ProsodicFluencyAnalyzer {
config: ProsodicConfig,
rhythm_analyzer: RhythmAnalyzer,
stress_analyzer: StressAnalyzer,
intonation_analyzer: IntonationAnalyzer,
timing_analyzer: TimingAnalyzer,
phonological_analyzer: PhonologicalAnalyzer,
advanced_analyzer: AdvancedProsodicAnalyzer,
analysis_orchestrator: AnalysisOrchestrator,
result_synthesizer: ResultSynthesizer,
component_cache: HashMap<String, ComponentCache>,
analysis_cache: HashMap<u64, ProsodicFluencyResult>,
analysis_stats: AnalysisStatistics,
}
#[derive(Debug, Clone)]
pub struct AnalysisOrchestrator {
execution_strategy: ExecutionStrategy,
component_dependencies: HashMap<String, Vec<String>>,
parallel_execution_config: ParallelExecutionConfig,
error_recovery_strategy: ErrorRecoveryStrategy,
}
#[derive(Debug, Clone)]
pub struct ResultSynthesizer {
synthesis_weights: HashMap<String, f64>,
integration_algorithms: Vec<IntegrationAlgorithm>,
quality_assessment: QualityAssessment,
confidence_calculator: ConfidenceCalculator,
}
#[derive(Debug, Clone)]
pub struct ComponentCache {
cache_data: HashMap<u64, Box<dyn CacheableResult>>,
cache_stats: CacheStatistics,
eviction_policy: EvictionPolicy,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct AnalysisStatistics {
pub total_analyses: u64,
pub component_execution_times: HashMap<String, f64>,
pub cache_hit_rates: HashMap<String, f64>,
pub error_rates: HashMap<String, f64>,
pub quality_scores: HashMap<String, f64>,
}
#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
pub enum ExecutionStrategy {
Sequential,
Parallel,
Adaptive,
PriorityBased,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ParallelExecutionConfig {
pub max_concurrent_components: usize,
pub thread_pool_size: usize,
pub load_balancing_strategy: LoadBalancingStrategy,
pub resource_allocation: ResourceAllocation,
}
#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
pub enum ErrorRecoveryStrategy {
FailFast,
BestEffort,
GracefulDegradation,
ComponentIsolation,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct IntegrationAlgorithm {
pub name: String,
pub algorithm_type: String,
pub input_components: Vec<String>,
pub weight_scheme: WeightScheme,
pub normalization_method: NormalizationMethod,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct QualityAssessment {
pub quality_metrics: Vec<String>,
pub assessment_thresholds: HashMap<String, f64>,
pub quality_weights: HashMap<String, f64>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ConfidenceCalculator {
pub confidence_factors: Vec<String>,
pub factor_weights: HashMap<String, f64>,
pub base_confidence: f64,
pub uncertainty_penalties: HashMap<String, f64>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct CacheStatistics {
pub hit_count: u64,
pub miss_count: u64,
pub eviction_count: u64,
pub memory_usage: usize,
}
#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
pub enum EvictionPolicy {
LRU,
LFU,
FIFO,
TimeBasedExpiry,
SizeBasedEviction,
}
#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
pub enum LoadBalancingStrategy {
RoundRobin,
LeastLoaded,
WeightedDistribution,
AdaptiveBalancing,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ResourceAllocation {
pub memory_limits: HashMap<String, usize>,
pub cpu_allocation: HashMap<String, f64>,
pub priority_levels: HashMap<String, i32>,
}
#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
pub enum WeightScheme {
Equal,
PriorityBased,
PerformanceBased,
AdaptiveWeighting,
ContextualWeighting,
}
#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
pub enum NormalizationMethod {
MinMax,
ZScore,
RobustScaling,
QuantileNormalization,
UnitVector,
}
pub trait CacheableResult: std::fmt::Debug + Send + Sync {
fn cache_key(&self) -> u64;
fn cache_size(&self) -> usize;
fn is_valid(&self) -> bool;
fn clone_boxed(&self) -> Box<dyn CacheableResult>;
fn as_any(&self) -> &dyn std::any::Any;
}
#[derive(Debug, Error)]
pub enum ProsodicFluencyError {
#[error("Configuration error: {message}")]
ConfigurationError { message: String },
#[error("Component analysis failed: {component} - {error}")]
ComponentAnalysisError { component: String, error: String },
#[error("Result synthesis failed: {synthesis_type}")]
ResultSynthesisError { synthesis_type: String },
#[error("Cache operation failed: {operation} - {details}")]
CacheError { operation: String, details: String },
#[error("Parallel execution failed: {component}")]
ParallelExecutionError { component: String },
#[error("Invalid input data: {data_type} - {reason}")]
InvalidInputError { data_type: String, reason: String },
#[error("Analysis timeout: {component} exceeded {timeout_ms}ms")]
TimeoutError { component: String, timeout_ms: u64 },
#[error("Resource exhaustion: {resource_type}")]
ResourceExhaustionError { resource_type: String },
}
impl ProsodicFluencyAnalyzer {
pub fn new(config: ProsodicConfig) -> Self {
let rhythm_analyzer = RhythmAnalyzer::new(config.rhythm.clone());
let stress_analyzer = StressAnalyzer::new(config.stress.clone());
let intonation_analyzer = IntonationAnalyzer::new(config.intonation.clone());
let timing_analyzer = TimingAnalyzer::new(config.timing.clone());
let phonological_analyzer = PhonologicalAnalyzer::new(config.phonological.clone());
let advanced_analyzer = AdvancedProsodicAnalyzer::new(config.advanced.clone());
let analysis_orchestrator = AnalysisOrchestrator::new(&config);
let result_synthesizer = ResultSynthesizer::new(&config);
Self {
config,
rhythm_analyzer,
stress_analyzer,
intonation_analyzer,
timing_analyzer,
phonological_analyzer,
advanced_analyzer,
analysis_orchestrator,
result_synthesizer,
component_cache: HashMap::new(),
analysis_cache: HashMap::new(),
analysis_stats: AnalysisStatistics::new(),
}
}
pub fn analyze(
&mut self,
text: &str,
phonetic_transcription: &str,
) -> Result<ProsodicFluencyResult, ProsodicFluencyError> {
self.validate_inputs(text, phonetic_transcription)?;
let cache_key = self.generate_cache_key(text, phonetic_transcription);
if let Some(cached_result) = self.analysis_cache.get(&cache_key) {
self.analysis_stats.update_cache_hit("main_analysis");
return Ok(cached_result.clone());
}
let analysis_start = std::time::Instant::now();
let component_results = self.execute_component_analyses(text, phonetic_transcription)?;
let synthesized_result = self.synthesize_results(component_results)?;
let analysis_duration = analysis_start.elapsed();
self.analysis_stats
.update_analysis_time("total_analysis", analysis_duration.as_secs_f64());
self.analysis_stats.increment_analysis_count();
if self.config.general.enable_caching {
self.analysis_cache
.insert(cache_key, synthesized_result.clone());
}
Ok(synthesized_result)
}
pub fn analyze_comprehensive(
&mut self,
text: &str,
phonetic_transcription: &str,
audio_features: Option<&AudioFeatures>,
) -> Result<ProsodicFluencyResult, ProsodicFluencyError> {
let mut result = self.analyze(text, phonetic_transcription)?;
if let Some(audio) = audio_features {
self.enhance_with_audio_features(&mut result, audio)?;
}
Ok(result)
}
fn execute_component_analyses(
&mut self,
text: &str,
phonetic_transcription: &str,
) -> Result<ComponentAnalysisResults, ProsodicFluencyError> {
match self.analysis_orchestrator.execution_strategy {
ExecutionStrategy::Sequential => {
self.execute_sequential_analysis(text, phonetic_transcription)
}
ExecutionStrategy::Parallel => {
self.execute_parallel_analysis(text, phonetic_transcription)
}
ExecutionStrategy::Adaptive => {
self.execute_adaptive_analysis(text, phonetic_transcription)
}
ExecutionStrategy::PriorityBased => {
self.execute_priority_based_analysis(text, phonetic_transcription)
}
}
}
fn execute_sequential_analysis(
&mut self,
text: &str,
phonetic_transcription: &str,
) -> Result<ComponentAnalysisResults, ProsodicFluencyError> {
let mut results = ComponentAnalysisResults::new();
for component in &self.analysis_orchestrator.get_execution_order() {
let component_result =
self.execute_component_analysis(component, text, phonetic_transcription)?;
results.insert(component.clone(), component_result);
}
Ok(results)
}
fn execute_parallel_analysis(
&mut self,
text: &str,
phonetic_transcription: &str,
) -> Result<ComponentAnalysisResults, ProsodicFluencyError> {
let mut results = ComponentAnalysisResults::new();
let execution_groups = self.analysis_orchestrator.get_parallel_execution_groups();
for group in execution_groups {
for component in group {
let component_result =
self.execute_component_analysis(&component, text, phonetic_transcription)?;
results.insert(component, component_result);
}
}
Ok(results)
}
fn execute_adaptive_analysis(
&mut self,
text: &str,
phonetic_transcription: &str,
) -> Result<ComponentAnalysisResults, ProsodicFluencyError> {
let text_complexity = self.assess_text_complexity(text, phonetic_transcription);
let strategy = if text_complexity > 0.8 {
ExecutionStrategy::Parallel
} else if text_complexity > 0.5 {
ExecutionStrategy::PriorityBased
} else {
ExecutionStrategy::Sequential
};
let original_strategy = self.analysis_orchestrator.execution_strategy;
self.analysis_orchestrator.execution_strategy = strategy;
let result = match strategy {
ExecutionStrategy::Parallel => {
self.execute_parallel_analysis(text, phonetic_transcription)
}
ExecutionStrategy::PriorityBased => {
self.execute_priority_based_analysis(text, phonetic_transcription)
}
_ => self.execute_sequential_analysis(text, phonetic_transcription),
};
self.analysis_orchestrator.execution_strategy = original_strategy;
result
}
fn execute_priority_based_analysis(
&mut self,
text: &str,
phonetic_transcription: &str,
) -> Result<ComponentAnalysisResults, ProsodicFluencyError> {
let mut results = ComponentAnalysisResults::new();
let priority_order = self.analysis_orchestrator.get_priority_order();
for component in priority_order {
let component_result =
self.execute_component_analysis(&component, text, phonetic_transcription)?;
results.insert(component, component_result);
}
Ok(results)
}
fn execute_component_analysis(
&mut self,
component: &str,
text: &str,
phonetic_transcription: &str,
) -> Result<Box<dyn CacheableResult>, ProsodicFluencyError> {
let component_start = std::time::Instant::now();
let result = match component {
"rhythm" => {
let metrics = self
.rhythm_analyzer
.analyze(text, phonetic_transcription)
.map_err(|e| ProsodicFluencyError::ComponentAnalysisError {
component: component.to_string(),
error: e.to_string(),
})?;
Box::new(CacheableRhythmMetrics(metrics)) as Box<dyn CacheableResult>
}
"stress" => {
let metrics = self
.stress_analyzer
.analyze(text, phonetic_transcription)
.map_err(|e| ProsodicFluencyError::ComponentAnalysisError {
component: component.to_string(),
error: e.to_string(),
})?;
Box::new(CacheableStressMetrics(metrics)) as Box<dyn CacheableResult>
}
"intonation" => {
let metrics = self
.intonation_analyzer
.analyze(text, phonetic_transcription)
.map_err(|e| ProsodicFluencyError::ComponentAnalysisError {
component: component.to_string(),
error: e.to_string(),
})?;
Box::new(CacheableIntonationMetrics(metrics)) as Box<dyn CacheableResult>
}
"timing" => {
let metrics = self
.timing_analyzer
.analyze(text, phonetic_transcription)
.map_err(|e| ProsodicFluencyError::ComponentAnalysisError {
component: component.to_string(),
error: e.to_string(),
})?;
Box::new(CacheableTimingMetrics(metrics)) as Box<dyn CacheableResult>
}
"phonological" => {
let metrics = self
.phonological_analyzer
.analyze(text, phonetic_transcription)
.map_err(|e| ProsodicFluencyError::ComponentAnalysisError {
component: component.to_string(),
error: e.to_string(),
})?;
Box::new(CacheablePhonologicalMetrics(metrics)) as Box<dyn CacheableResult>
}
_ => {
return Err(ProsodicFluencyError::ComponentAnalysisError {
component: component.to_string(),
error: "Unknown component".to_string(),
})
}
};
let component_duration = component_start.elapsed();
self.analysis_stats
.update_analysis_time(component, component_duration.as_secs_f64());
Ok(result)
}
fn synthesize_results(
&mut self,
component_results: ComponentAnalysisResults,
) -> Result<ProsodicFluencyResult, ProsodicFluencyError> {
self.result_synthesizer.synthesize(component_results)
}
fn validate_inputs(
&self,
text: &str,
phonetic_transcription: &str,
) -> Result<(), ProsodicFluencyError> {
if text.is_empty() {
return Err(ProsodicFluencyError::InvalidInputError {
data_type: "text".to_string(),
reason: "Text cannot be empty".to_string(),
});
}
if phonetic_transcription.is_empty() {
return Err(ProsodicFluencyError::InvalidInputError {
data_type: "phonetic_transcription".to_string(),
reason: "Phonetic transcription cannot be empty".to_string(),
});
}
Ok(())
}
fn generate_cache_key(&self, text: &str, phonetic_transcription: &str) -> u64 {
let mut hasher = std::collections::hash_map::DefaultHasher::new();
text.hash(&mut hasher);
phonetic_transcription.hash(&mut hasher);
self.config.general.analysis_depth.hash(&mut hasher);
hasher.finish()
}
fn assess_text_complexity(&self, text: &str, phonetic_transcription: &str) -> f64 {
let word_count = text.split_whitespace().count();
let phoneme_count = phonetic_transcription.split_whitespace().count();
let avg_word_length = text.len() as f64 / word_count.max(1) as f64;
let length_complexity = (word_count as f64 / 100.0).min(1.0);
let phonemic_complexity = (phoneme_count as f64 / word_count.max(1) as f64 / 5.0).min(1.0);
let structural_complexity = (avg_word_length / 8.0).min(1.0);
(length_complexity + phonemic_complexity + structural_complexity) / 3.0
}
fn enhance_with_audio_features(
&self,
result: &mut ProsodicFluencyResult,
audio_features: &AudioFeatures,
) -> Result<(), ProsodicFluencyError> {
if let Some(beat_times) = &audio_features.beat_times {
result.rhythm_metrics.audio_confirmed_beats = Some(beat_times.clone());
}
if let Some(f0_contour) = &audio_features.f0_contour {
result.intonation_metrics.measured_f0_contour = Some(f0_contour.clone());
}
if let Some(pause_durations) = &audio_features.pause_durations {
result.timing_metrics.measured_pause_durations = Some(pause_durations.clone());
}
Ok(())
}
pub fn update_config(&mut self, new_config: ProsodicConfig) {
self.config = new_config;
self.rhythm_analyzer
.update_config(self.config.rhythm.clone());
self.stress_analyzer
.update_config(self.config.stress.clone());
self.intonation_analyzer
.update_config(self.config.intonation.clone());
self.timing_analyzer
.update_config(self.config.timing.clone());
self.phonological_analyzer
.update_config(self.config.phonological.clone());
self.advanced_analyzer
.update_config(self.config.advanced.clone());
self.analysis_orchestrator.update_config(&self.config);
self.result_synthesizer.update_config(&self.config);
if self.config.general.clear_cache_on_config_change {
self.clear_all_caches();
}
}
pub fn clear_all_caches(&mut self) {
self.analysis_cache.clear();
self.component_cache.clear();
self.rhythm_analyzer.clear_cache();
self.stress_analyzer.clear_cache();
self.intonation_analyzer.clear_cache();
self.timing_analyzer.clear_cache();
self.phonological_analyzer.clear_cache();
self.advanced_analyzer.clear_cache();
}
pub fn get_analysis_statistics(&self) -> &AnalysisStatistics {
&self.analysis_stats
}
pub fn get_cache_statistics(&self) -> HashMap<String, CacheStatistics> {
let mut stats = HashMap::new();
for (component, cache) in &self.component_cache {
stats.insert(component.clone(), cache.cache_stats.clone());
}
stats.insert(
"main_analysis".to_string(),
CacheStatistics {
hit_count: 0, miss_count: 0,
eviction_count: 0,
memory_usage: self.analysis_cache.len()
* std::mem::size_of::<ProsodicFluencyResult>(),
},
);
stats
}
}
pub type ComponentAnalysisResults = HashMap<String, Box<dyn CacheableResult>>;
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct AudioFeatures {
pub beat_times: Option<Vec<f64>>,
pub f0_contour: Option<Vec<f64>>,
pub pause_durations: Option<Vec<f64>>,
pub intensity_contour: Option<Vec<f64>>,
pub spectral_features: Option<SpectralFeatures>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SpectralFeatures {
pub spectral_centroid: Vec<f64>,
pub spectral_rolloff: Vec<f64>,
pub mfccs: Vec<Vec<f64>>,
pub spectral_contrast: Vec<f64>,
}
#[derive(Debug, Clone)]
pub struct CacheableRhythmMetrics(pub RhythmMetrics);
#[derive(Debug, Clone)]
pub struct CacheableStressMetrics(pub StressMetrics);
#[derive(Debug, Clone)]
pub struct CacheableIntonationMetrics(pub IntonationMetrics);
#[derive(Debug, Clone)]
pub struct CacheableTimingMetrics(pub TimingMetrics);
#[derive(Debug, Clone)]
pub struct CacheablePhonologicalMetrics(pub PhonologicalMetrics);
impl CacheableResult for CacheableRhythmMetrics {
fn cache_key(&self) -> u64 {
let mut hasher = std::collections::hash_map::DefaultHasher::new();
self.0.beat_consistency.to_bits().hash(&mut hasher);
hasher.finish()
}
fn cache_size(&self) -> usize {
std::mem::size_of::<RhythmMetrics>()
}
fn is_valid(&self) -> bool {
!self.0.beat_consistency.is_nan()
}
fn clone_boxed(&self) -> Box<dyn CacheableResult> {
Box::new(self.clone())
}
fn as_any(&self) -> &dyn std::any::Any {
self
}
}
impl CacheableResult for CacheableStressMetrics {
fn cache_key(&self) -> u64 {
let mut hasher = std::collections::hash_map::DefaultHasher::new();
self.0.stress_prominence_score.to_bits().hash(&mut hasher);
hasher.finish()
}
fn cache_size(&self) -> usize {
std::mem::size_of::<StressMetrics>()
}
fn is_valid(&self) -> bool {
!self.0.stress_prominence_score.is_nan()
}
fn clone_boxed(&self) -> Box<dyn CacheableResult> {
Box::new(self.clone())
}
fn as_any(&self) -> &dyn std::any::Any {
self
}
}
impl CacheableResult for CacheableIntonationMetrics {
fn cache_key(&self) -> u64 {
let mut hasher = std::collections::hash_map::DefaultHasher::new();
self.0.pitch_range.to_bits().hash(&mut hasher);
hasher.finish()
}
fn cache_size(&self) -> usize {
std::mem::size_of::<IntonationMetrics>()
}
fn is_valid(&self) -> bool {
!self.0.pitch_range.is_nan()
}
fn clone_boxed(&self) -> Box<dyn CacheableResult> {
Box::new(self.clone())
}
fn as_any(&self) -> &dyn std::any::Any {
self
}
}
impl CacheableResult for CacheableTimingMetrics {
fn cache_key(&self) -> u64 {
let mut hasher = std::collections::hash_map::DefaultHasher::new();
self.0.speech_rate.to_bits().hash(&mut hasher);
hasher.finish()
}
fn cache_size(&self) -> usize {
std::mem::size_of::<TimingMetrics>()
}
fn is_valid(&self) -> bool {
!self.0.speech_rate.is_nan()
}
fn clone_boxed(&self) -> Box<dyn CacheableResult> {
Box::new(self.clone())
}
fn as_any(&self) -> &dyn std::any::Any {
self
}
}
impl CacheableResult for CacheablePhonologicalMetrics {
fn cache_key(&self) -> u64 {
let mut hasher = std::collections::hash_map::DefaultHasher::new();
self.0.complexity_score.to_bits().hash(&mut hasher);
hasher.finish()
}
fn cache_size(&self) -> usize {
std::mem::size_of::<PhonologicalMetrics>()
}
fn is_valid(&self) -> bool {
!self.0.complexity_score.is_nan()
}
fn clone_boxed(&self) -> Box<dyn CacheableResult> {
Box::new(self.clone())
}
fn as_any(&self) -> &dyn std::any::Any {
self
}
}
impl AnalysisOrchestrator {
fn new(config: &ProsodicConfig) -> Self {
Self {
execution_strategy: config.general.execution_strategy,
component_dependencies: Self::create_component_dependencies(),
parallel_execution_config: ParallelExecutionConfig::default(),
error_recovery_strategy: ErrorRecoveryStrategy::BestEffort,
}
}
fn get_execution_order(&self) -> Vec<String> {
vec![
"rhythm".to_string(),
"stress".to_string(),
"intonation".to_string(),
"timing".to_string(),
"phonological".to_string(),
"advanced".to_string(),
]
}
fn get_parallel_execution_groups(&self) -> Vec<Vec<String>> {
vec![
vec!["rhythm".to_string(), "timing".to_string()], vec![
"stress".to_string(),
"intonation".to_string(),
"phonological".to_string(),
], vec!["advanced".to_string()], ]
}
fn get_priority_order(&self) -> Vec<String> {
vec![
"stress".to_string(), "intonation".to_string(), "rhythm".to_string(), "timing".to_string(), "phonological".to_string(), "advanced".to_string(), ]
}
fn create_component_dependencies() -> HashMap<String, Vec<String>> {
let mut deps = HashMap::new();
deps.insert("rhythm".to_string(), vec![]);
deps.insert("stress".to_string(), vec![]);
deps.insert("intonation".to_string(), vec![]);
deps.insert("timing".to_string(), vec![]);
deps.insert("phonological".to_string(), vec![]);
deps.insert(
"advanced".to_string(),
vec![
"rhythm".to_string(),
"stress".to_string(),
"intonation".to_string(),
"timing".to_string(),
"phonological".to_string(),
],
);
deps
}
fn update_config(&mut self, config: &ProsodicConfig) {
self.execution_strategy = config.general.execution_strategy;
}
}
impl ResultSynthesizer {
fn new(config: &ProsodicConfig) -> Self {
Self {
synthesis_weights: Self::create_synthesis_weights(),
integration_algorithms: Self::create_integration_algorithms(),
quality_assessment: QualityAssessment::default(),
confidence_calculator: ConfidenceCalculator::default(),
}
}
fn synthesize(
&self,
component_results: ComponentAnalysisResults,
) -> Result<ProsodicFluencyResult, ProsodicFluencyError> {
let rhythm_metrics = self.extract_rhythm_metrics(&component_results)?;
let stress_metrics = self.extract_stress_metrics(&component_results)?;
let intonation_metrics = self.extract_intonation_metrics(&component_results)?;
let timing_metrics = self.extract_timing_metrics(&component_results)?;
let phonological_metrics = self.extract_phonological_metrics(&component_results)?;
let overall_fluency_score = self.calculate_overall_fluency_score(
&rhythm_metrics,
&stress_metrics,
&intonation_metrics,
&timing_metrics,
&phonological_metrics,
);
let analysis_confidence = self.calculate_analysis_confidence(
&rhythm_metrics,
&stress_metrics,
&intonation_metrics,
&timing_metrics,
&phonological_metrics,
);
let fluency_level = self.determine_fluency_level(overall_fluency_score);
let fluency_insights = self.generate_fluency_insights(
&rhythm_metrics,
&stress_metrics,
&intonation_metrics,
&timing_metrics,
&phonological_metrics,
);
Ok(ProsodicFluencyResult {
overall_fluency_score,
analysis_confidence,
fluency_level,
rhythm_metrics,
stress_metrics,
intonation_metrics,
timing_metrics,
phonological_metrics,
advanced_metrics: None, fluency_insights,
component_contributions: self.calculate_component_contributions(&component_results),
})
}
fn extract_rhythm_metrics(
&self,
results: &ComponentAnalysisResults,
) -> Result<RhythmMetrics, ProsodicFluencyError> {
let result =
results
.get("rhythm")
.ok_or_else(|| ProsodicFluencyError::ResultSynthesisError {
synthesis_type: "rhythm_metrics_extraction".to_string(),
})?;
if let Some(cacheable_rhythm) = result.as_any().downcast_ref::<CacheableRhythmMetrics>() {
Ok(cacheable_rhythm.0.clone())
} else {
Err(ProsodicFluencyError::ResultSynthesisError {
synthesis_type: "rhythm_metrics_cast".to_string(),
})
}
}
fn extract_stress_metrics(
&self,
results: &ComponentAnalysisResults,
) -> Result<StressMetrics, ProsodicFluencyError> {
let result =
results
.get("stress")
.ok_or_else(|| ProsodicFluencyError::ResultSynthesisError {
synthesis_type: "stress_metrics_extraction".to_string(),
})?;
if let Some(cacheable_stress) = result.as_any().downcast_ref::<CacheableStressMetrics>() {
Ok(cacheable_stress.0.clone())
} else {
Err(ProsodicFluencyError::ResultSynthesisError {
synthesis_type: "stress_metrics_cast".to_string(),
})
}
}
fn extract_intonation_metrics(
&self,
results: &ComponentAnalysisResults,
) -> Result<IntonationMetrics, ProsodicFluencyError> {
let result = results.get("intonation").ok_or_else(|| {
ProsodicFluencyError::ResultSynthesisError {
synthesis_type: "intonation_metrics_extraction".to_string(),
}
})?;
if let Some(cacheable_intonation) =
result.as_any().downcast_ref::<CacheableIntonationMetrics>()
{
Ok(cacheable_intonation.0.clone())
} else {
Err(ProsodicFluencyError::ResultSynthesisError {
synthesis_type: "intonation_metrics_cast".to_string(),
})
}
}
fn extract_timing_metrics(
&self,
results: &ComponentAnalysisResults,
) -> Result<TimingMetrics, ProsodicFluencyError> {
let result =
results
.get("timing")
.ok_or_else(|| ProsodicFluencyError::ResultSynthesisError {
synthesis_type: "timing_metrics_extraction".to_string(),
})?;
if let Some(cacheable_timing) = result.as_any().downcast_ref::<CacheableTimingMetrics>() {
Ok(cacheable_timing.0.clone())
} else {
Err(ProsodicFluencyError::ResultSynthesisError {
synthesis_type: "timing_metrics_cast".to_string(),
})
}
}
fn extract_phonological_metrics(
&self,
results: &ComponentAnalysisResults,
) -> Result<PhonologicalMetrics, ProsodicFluencyError> {
let result = results.get("phonological").ok_or_else(|| {
ProsodicFluencyError::ResultSynthesisError {
synthesis_type: "phonological_metrics_extraction".to_string(),
}
})?;
if let Some(cacheable_phonological) = result
.as_any()
.downcast_ref::<CacheablePhonologicalMetrics>()
{
Ok(cacheable_phonological.0.clone())
} else {
Err(ProsodicFluencyError::ResultSynthesisError {
synthesis_type: "phonological_metrics_cast".to_string(),
})
}
}
fn calculate_overall_fluency_score(
&self,
rhythm_metrics: &RhythmMetrics,
stress_metrics: &StressMetrics,
intonation_metrics: &IntonationMetrics,
timing_metrics: &TimingMetrics,
phonological_metrics: &PhonologicalMetrics,
) -> f64 {
let weights = &self.synthesis_weights;
let rhythm_contribution =
rhythm_metrics.beat_consistency * weights.get("rhythm").unwrap_or(&0.2);
let stress_contribution =
stress_metrics.stress_consistency * weights.get("stress").unwrap_or(&0.25);
let intonation_contribution =
intonation_metrics.contour_smoothness * weights.get("intonation").unwrap_or(&0.25);
let timing_contribution =
timing_metrics.tempo_consistency * weights.get("timing").unwrap_or(&0.2);
let phonological_contribution = (1.0
- phonological_metrics
.phonotactic_constraints
.violation_density)
* weights.get("phonological").unwrap_or(&0.1);
(rhythm_contribution
+ stress_contribution
+ intonation_contribution
+ timing_contribution
+ phonological_contribution)
.max(0.0)
.min(1.0)
}
fn calculate_analysis_confidence(
&self,
rhythm_metrics: &RhythmMetrics,
stress_metrics: &StressMetrics,
intonation_metrics: &IntonationMetrics,
timing_metrics: &TimingMetrics,
phonological_metrics: &PhonologicalMetrics,
) -> f64 {
let rhythm_confidence = if rhythm_metrics.beat_consistency > 0.7 {
0.9
} else {
0.7
};
let stress_confidence = if stress_metrics.stress_consistency > 0.6 {
0.85
} else {
0.65
};
let intonation_confidence = if intonation_metrics.contour_smoothness > 0.8 {
0.9
} else {
0.75
};
let timing_confidence = if timing_metrics.tempo_consistency > 0.7 {
0.88
} else {
0.7
};
let phonological_confidence = if phonological_metrics
.phonotactic_constraints
.violation_density
< 0.2
{
0.9
} else {
0.8
};
(rhythm_confidence
+ stress_confidence
+ intonation_confidence
+ timing_confidence
+ phonological_confidence)
/ 5.0
}
fn determine_fluency_level(&self, overall_score: f64) -> FluencyLevel {
match overall_score {
score if score >= 0.85 => FluencyLevel::Advanced,
score if score >= 0.70 => FluencyLevel::Proficient,
score if score >= 0.55 => FluencyLevel::Intermediate,
score if score >= 0.40 => FluencyLevel::Elementary,
_ => FluencyLevel::Beginner,
}
}
fn generate_fluency_insights(
&self,
rhythm_metrics: &RhythmMetrics,
stress_metrics: &StressMetrics,
intonation_metrics: &IntonationMetrics,
timing_metrics: &TimingMetrics,
phonological_metrics: &PhonologicalMetrics,
) -> Vec<FluencyInsight> {
let mut insights = Vec::new();
if rhythm_metrics.beat_consistency < 0.6 {
insights.push(FluencyInsight {
category: "Rhythm".to_string(),
insight: "Beat patterns show irregular timing that may affect speech fluency"
.to_string(),
severity: InsightSeverity::Moderate,
confidence: 0.8,
});
}
if stress_metrics.stress_prominence_score < 0.5 {
insights.push(FluencyInsight {
category: "Stress".to_string(),
insight: "Weak stress prominence may reduce speech naturalness and clarity"
.to_string(),
severity: InsightSeverity::High,
confidence: 0.85,
});
}
if intonation_metrics.pitch_range < 50.0 {
insights.push(FluencyInsight {
category: "Intonation".to_string(),
insight: "Limited pitch range may result in monotonous speech patterns".to_string(),
severity: InsightSeverity::Moderate,
confidence: 0.75,
});
}
if timing_metrics.speech_rate > 6.0 {
insights.push(FluencyInsight {
category: "Timing".to_string(),
insight: "Speech rate is unusually fast, which may impact comprehensibility"
.to_string(),
severity: InsightSeverity::Low,
confidence: 0.7,
});
}
if phonological_metrics
.phonotactic_constraints
.violation_density
> 0.3
{
insights.push(FluencyInsight {
category: "Phonological".to_string(),
insight:
"High rate of phonotactic violations may indicate pronunciation difficulties"
.to_string(),
severity: InsightSeverity::High,
confidence: 0.9,
});
}
insights
}
fn calculate_component_contributions(
&self,
results: &ComponentAnalysisResults,
) -> HashMap<String, f64> {
let mut contributions = HashMap::new();
for (component, _) in results {
let weight = self.synthesis_weights.get(component).unwrap_or(&0.2);
contributions.insert(component.clone(), *weight);
}
contributions
}
fn create_synthesis_weights() -> HashMap<String, f64> {
let mut weights = HashMap::new();
weights.insert("rhythm".to_string(), 0.2);
weights.insert("stress".to_string(), 0.25);
weights.insert("intonation".to_string(), 0.25);
weights.insert("timing".to_string(), 0.2);
weights.insert("phonological".to_string(), 0.1);
weights
}
fn create_integration_algorithms() -> Vec<IntegrationAlgorithm> {
vec![IntegrationAlgorithm {
name: "Weighted Average".to_string(),
algorithm_type: "linear_combination".to_string(),
input_components: vec![
"rhythm".to_string(),
"stress".to_string(),
"intonation".to_string(),
],
weight_scheme: WeightScheme::PriorityBased,
normalization_method: NormalizationMethod::MinMax,
}]
}
fn update_config(&mut self, config: &ProsodicConfig) {
if config.general.adaptive_synthesis_weights {
for (component, weight) in &mut self.synthesis_weights {
*weight *= 1.0 + config.general.adaptation_rate;
}
}
}
}
impl AnalysisStatistics {
fn new() -> Self {
Self {
total_analyses: 0,
component_execution_times: HashMap::new(),
cache_hit_rates: HashMap::new(),
error_rates: HashMap::new(),
quality_scores: HashMap::new(),
}
}
fn increment_analysis_count(&mut self) {
self.total_analyses += 1;
}
fn update_analysis_time(&mut self, component: &str, duration: f64) {
self.component_execution_times
.insert(component.to_string(), duration);
}
fn update_cache_hit(&mut self, component: &str) {
let hit_rate = self
.cache_hit_rates
.entry(component.to_string())
.or_insert(0.0);
*hit_rate = (*hit_rate + 1.0) / 2.0; }
}
impl Default for ParallelExecutionConfig {
fn default() -> Self {
Self {
max_concurrent_components: 4,
thread_pool_size: std::thread::available_parallelism()
.map(|n| n.get())
.unwrap_or(4),
load_balancing_strategy: LoadBalancingStrategy::RoundRobin,
resource_allocation: ResourceAllocation::default(),
}
}
}
impl Default for ResourceAllocation {
fn default() -> Self {
Self {
memory_limits: HashMap::new(),
cpu_allocation: HashMap::new(),
priority_levels: HashMap::new(),
}
}
}
impl Default for QualityAssessment {
fn default() -> Self {
Self {
quality_metrics: vec!["consistency".to_string(), "accuracy".to_string()],
assessment_thresholds: HashMap::new(),
quality_weights: HashMap::new(),
}
}
}
impl Default for ConfidenceCalculator {
fn default() -> Self {
Self {
confidence_factors: vec![
"data_quality".to_string(),
"analysis_consistency".to_string(),
],
factor_weights: HashMap::new(),
base_confidence: 0.8,
uncertainty_penalties: HashMap::new(),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_prosodic_fluency_analyzer_creation() {
let config = ProsodicConfig::default();
let analyzer = ProsodicFluencyAnalyzer::new(config);
assert_eq!(analyzer.analysis_stats.total_analyses, 0);
assert!(analyzer.analysis_cache.is_empty());
}
#[test]
fn test_analysis_orchestrator_execution_order() {
let config = ProsodicConfig::default();
let orchestrator = AnalysisOrchestrator::new(&config);
let order = orchestrator.get_execution_order();
assert_eq!(order.len(), 6);
assert_eq!(order[0], "rhythm");
assert_eq!(order[5], "advanced");
}
#[test]
fn test_parallel_execution_groups() {
let config = ProsodicConfig::default();
let orchestrator = AnalysisOrchestrator::new(&config);
let groups = orchestrator.get_parallel_execution_groups();
assert_eq!(groups.len(), 3);
assert!(groups[0].contains(&"rhythm".to_string()));
assert!(groups[2].contains(&"advanced".to_string()));
}
#[test]
fn test_result_synthesizer_weights() {
let config = ProsodicConfig::default();
let synthesizer = ResultSynthesizer::new(&config);
assert!(synthesizer.synthesis_weights.contains_key("rhythm"));
assert!(synthesizer.synthesis_weights.contains_key("stress"));
let total_weight: f64 = synthesizer.synthesis_weights.values().sum();
assert!((total_weight - 1.0).abs() < 0.001); }
#[test]
fn test_cacheable_result_implementations() {
use crate::metrics::fluency::prosodic::rhythm::RhythmMetrics;
let rhythm_metrics = RhythmMetrics {
beat_consistency: 0.8,
beat_strength_variability: 0.2,
tempo_stability: 0.85,
rhythmic_complexity: 1.5,
detected_beat_patterns: vec!["strong-weak".to_string()],
rhythm_classification: "stress_timed".to_string(),
metrical_structure: "4/4".to_string(),
syncopation_frequency: 0.1,
microrhythmic_variations: vec![0.02, 0.015, 0.01],
};
let cacheable = CacheableRhythmMetrics(rhythm_metrics);
assert!(cacheable.is_valid());
assert!(cacheable.cache_size() > 0);
assert!(cacheable.cache_key() != 0);
}
}