1use crate::{LanguageCode, Phoneme, PhoneticFeatures, Result};
7use serde::{Deserialize, Serialize};
8use std::collections::{HashMap, VecDeque};
9use std::sync::{Arc, Mutex};
10use std::time::{Instant, SystemTime};
11use tokio::sync::mpsc;
12
13pub struct AdaptivePronunciationSystem {
15 pub language: LanguageCode,
17 pub correction_history: Arc<Mutex<VecDeque<UserCorrection>>>,
19 pub adaptation_model: AdaptationModel,
21 pub learning_rate: f32,
23 pub min_corrections_threshold: usize,
25 pub max_history_size: usize,
27}
28
29#[derive(Debug, Clone, Serialize, Deserialize)]
31pub struct UserCorrection {
32 pub text: String,
34 pub original_phonemes: Vec<Phoneme>,
36 pub corrected_phonemes: Vec<Phoneme>,
38 pub timestamp: SystemTime,
40 pub context: Option<String>,
42 pub user_confidence: f32,
44}
45
46#[derive(Debug, Clone, Serialize, Deserialize, Default)]
48pub struct AdaptationModel {
49 pub word_adaptations: HashMap<String, AdaptationRule>,
51 pub pattern_adaptations: Vec<PatternAdaptation>,
53 pub context_adaptations: HashMap<String, Vec<AdaptationRule>>,
55 pub stats: AdaptationStats,
57}
58
59#[derive(Debug, Clone, Serialize, Deserialize)]
61pub struct AdaptationRule {
62 pub source_phonemes: Vec<String>,
64 pub target_phonemes: Vec<String>,
66 pub strength: f32,
68 pub support_count: usize,
70 pub last_updated: SystemTime,
72}
73
74#[derive(Debug, Clone, Serialize, Deserialize)]
76pub struct PatternAdaptation {
77 pub source_pattern: String,
79 pub target_pattern: String,
81 pub confidence: f32,
83 pub usage_count: usize,
85}
86
87#[derive(Debug, Clone, Default, Serialize, Deserialize)]
89pub struct AdaptationStats {
90 pub total_corrections: usize,
92 pub total_adaptations: usize,
94 pub avg_confidence: f32,
96 pub frequent_adaptations: HashMap<String, usize>,
98}
99
100pub struct MultilingualPhonemeMapper {
102 pub cross_lang_mappings: HashMap<(LanguageCode, LanguageCode), PhonemeMapping>,
104 pub universal_phonemes: UniversalPhonemeInventory,
106 pub language_systems: HashMap<LanguageCode, LanguagePhonemeSystem>,
108}
109
110#[derive(Debug, Clone, Serialize, Deserialize)]
112pub struct PhonemeMapping {
113 pub direct_mappings: HashMap<String, String>,
115 pub approximate_mappings: HashMap<String, Vec<(String, f32)>>,
117 pub context_mappings: HashMap<String, Vec<ContextualMapping>>,
119}
120
121#[derive(Debug, Clone, Serialize, Deserialize)]
123pub struct ContextualMapping {
124 pub source: String,
126 pub target: String,
128 pub context_conditions: Vec<String>,
130 pub confidence: f32,
132}
133
134#[derive(Debug, Clone, Serialize, Deserialize, Default)]
136pub struct UniversalPhonemeInventory {
137 pub universal_phonemes: HashMap<String, UniversalPhoneme>,
139 pub feature_matrix: HashMap<String, PhoneticFeatures>,
141 pub similarity_matrix: HashMap<(String, String), f32>,
143}
144
145#[derive(Debug, Clone, Serialize, Deserialize)]
147pub struct UniversalPhoneme {
148 pub ipa_symbol: String,
150 pub features: PhoneticFeatures,
152 pub language_realizations: HashMap<LanguageCode, Vec<String>>,
154 pub articulatory_description: String,
156}
157
158#[derive(Debug, Clone, Serialize, Deserialize)]
160pub struct LanguagePhonemeSystem {
161 pub language: LanguageCode,
163 pub native_phonemes: Vec<String>,
165 pub allophones: HashMap<String, Vec<String>>,
167 pub phonotactic_rules: Vec<PhonotacticRule>,
169 pub stress_patterns: StressPatternSystem,
171}
172
173#[derive(Debug, Clone, Serialize, Deserialize)]
175pub struct PhonotacticRule {
176 pub rule_type: String,
178 pub allowed_sequences: Vec<Vec<String>>,
180 pub forbidden_sequences: Vec<Vec<String>>,
182 pub strength: f32,
184}
185
186#[derive(Debug, Clone, Serialize, Deserialize)]
188pub struct StressPatternSystem {
189 pub default_pattern: Vec<u8>,
191 pub word_rules: HashMap<String, Vec<u8>>,
193 pub pattern_rules: Vec<StressPatternRule>,
195}
196
197#[derive(Debug, Clone, Serialize, Deserialize)]
199pub struct StressPatternRule {
200 pub word_pattern: String,
202 pub stress_pattern: Vec<u8>,
204 pub confidence: f32,
206}
207
208pub struct PhonemeQualityScorer {
210 pub quality_models: HashMap<LanguageCode, QualityModel>,
212 pub global_factors: GlobalQualityFactors,
214 pub assessment_history: VecDeque<QualityAssessment>,
216}
217
218#[derive(Debug, Clone, Serialize, Deserialize)]
220pub struct QualityModel {
221 pub phoneme_factors: HashMap<String, PhonemeQualityFactors>,
223 pub sequence_patterns: Vec<SequenceQualityPattern>,
225 pub context_adjustments: HashMap<String, f32>,
227}
228
229#[derive(Debug, Clone, Serialize, Deserialize)]
231pub struct PhonemeQualityFactors {
232 pub base_quality: f32,
234 pub frequency_factor: f32,
236 pub complexity_factor: f32,
238 pub stability_factor: f32,
240}
241
242#[derive(Debug, Clone, Serialize, Deserialize)]
244pub struct SequenceQualityPattern {
245 pub sequence_pattern: Vec<String>,
247 pub quality_modifier: f32,
249 pub confidence: f32,
251}
252
253#[derive(Debug, Clone, Serialize, Deserialize, Default)]
255pub struct GlobalQualityFactors {
256 pub naturalness_weights: HashMap<String, f32>,
258 pub distinctiveness_factors: HashMap<String, f32>,
260 pub acoustic_factors: HashMap<String, f32>,
262}
263
264#[derive(Debug, Clone, Serialize, Deserialize)]
266pub struct QualityAssessment {
267 pub text: String,
269 pub phonemes: Vec<Phoneme>,
271 pub overall_score: f32,
273 pub phoneme_scores: Vec<f32>,
275 pub factor_breakdown: HashMap<String, f32>,
277 pub timestamp: SystemTime,
279}
280
281pub struct StreamingG2pProcessor {
283 pub language: LanguageCode,
285 pub text_buffer: Arc<Mutex<String>>,
287 pub phoneme_sender: mpsc::UnboundedSender<StreamingPhoneme>,
289 pub config: StreamingConfig,
291 pub stats: Arc<Mutex<StreamingStats>>,
293}
294
295#[derive(Debug, Clone, Serialize, Deserialize)]
297pub struct StreamingPhoneme {
298 pub phoneme: Phoneme,
300 pub start_time_ms: f32,
302 pub duration_ms: f32,
304 pub streaming_confidence: f32,
306 pub is_word_boundary: bool,
308}
309
310#[derive(Debug, Clone, Serialize, Deserialize)]
312pub struct StreamingConfig {
313 pub buffer_size: usize,
315 pub chunk_size: usize,
317 pub lookahead_size: usize,
319 pub min_latency_ms: f32,
321 pub max_latency_ms: f32,
323 pub enable_adaptation: bool,
325}
326
327#[derive(Debug, Clone, Default, Serialize, Deserialize)]
329pub struct StreamingStats {
330 pub total_chars_processed: usize,
332 pub total_phonemes_generated: usize,
334 pub avg_latency_ms: f32,
336 pub peak_latency_ms: f32,
338 pub throughput_cps: f32,
340 pub buffer_utilization: f32,
342}
343
344pub struct EmotionAwareG2pProcessor {
346 pub base_processor: Arc<dyn crate::G2p>,
348 pub emotion_classifier: EmotionClassifier,
350 pub emotion_modifications: HashMap<EmotionType, EmotionModification>,
352 pub processing_history: VecDeque<EmotionProcessingEntry>,
354}
355
356#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
358pub enum EmotionType {
359 Neutral,
361 Happy,
363 Sad,
365 Angry,
367 Fearful,
369 Surprised,
371 Disgusted,
373 Excited,
375 Calm,
377}
378
379#[derive(Debug, Clone, Serialize, Deserialize, Default)]
381pub struct EmotionClassifier {
382 pub emotion_keywords: HashMap<EmotionType, HashMap<String, f32>>,
384 pub punctuation_indicators: HashMap<String, HashMap<EmotionType, f32>>,
386 pub syntax_indicators: Vec<SyntaxEmotionPattern>,
388 pub classification_thresholds: HashMap<EmotionType, f32>,
390}
391
392#[derive(Debug, Clone, Serialize, Deserialize)]
394pub struct SyntaxEmotionPattern {
395 pub pattern: String,
397 pub emotion_scores: HashMap<EmotionType, f32>,
399 pub confidence: f32,
401}
402
403#[derive(Debug, Clone, Serialize, Deserialize)]
405pub struct EmotionModification {
406 pub pitch_modifications: HashMap<String, f32>,
408 pub duration_modifications: HashMap<String, f32>,
410 pub stress_modifications: HashMap<String, u8>,
412 pub voice_quality_adjustments: VoiceQualityAdjustments,
414}
415
416#[derive(Debug, Clone, Serialize, Deserialize)]
418pub struct VoiceQualityAdjustments {
419 pub breathiness: f32,
421 pub creakiness: f32,
423 pub tenseness: f32,
425 pub nasality: f32,
427}
428
429#[derive(Debug, Clone, Serialize, Deserialize)]
431pub struct EmotionProcessingEntry {
432 pub text: String,
434 pub detected_emotion: EmotionType,
436 pub emotion_confidence: f32,
438 pub original_phonemes: Vec<Phoneme>,
440 pub modified_phonemes: Vec<Phoneme>,
442 pub timestamp: SystemTime,
444}
445
446impl AdaptivePronunciationSystem {
448 pub fn new(language: LanguageCode) -> Self {
450 Self {
451 language,
452 correction_history: Arc::new(Mutex::new(VecDeque::new())),
453 adaptation_model: AdaptationModel::default(),
454 learning_rate: 0.1,
455 min_corrections_threshold: 3,
456 max_history_size: 1000,
457 }
458 }
459
460 pub fn add_correction(&mut self, correction: UserCorrection) -> Result<()> {
462 {
463 let mut history = self
464 .correction_history
465 .lock()
466 .expect("lock should not be poisoned");
467
468 history.push_back(correction.clone());
470
471 if history.len() > self.max_history_size {
473 history.pop_front();
474 }
475 } self.update_adaptation_model(&correction)?;
479
480 Ok(())
481 }
482
483 fn update_adaptation_model(&mut self, correction: &UserCorrection) -> Result<()> {
485 let source_phonemes: Vec<String> = correction
487 .original_phonemes
488 .iter()
489 .map(|p| p.symbol.clone())
490 .collect();
491
492 let target_phonemes: Vec<String> = correction
493 .corrected_phonemes
494 .iter()
495 .map(|p| p.symbol.clone())
496 .collect();
497
498 let key = correction.text.to_lowercase();
500 if let Some(existing_rule) = self.adaptation_model.word_adaptations.get_mut(&key) {
501 existing_rule.support_count += 1;
502 existing_rule.strength = (existing_rule.strength + correction.user_confidence) / 2.0;
503 existing_rule.last_updated = SystemTime::now();
504 } else {
505 let new_rule = AdaptationRule {
506 source_phonemes,
507 target_phonemes,
508 strength: correction.user_confidence,
509 support_count: 1,
510 last_updated: SystemTime::now(),
511 };
512 self.adaptation_model.word_adaptations.insert(key, new_rule);
513 }
514
515 self.adaptation_model.stats.total_corrections += 1;
517
518 Ok(())
519 }
520
521 pub fn apply_adaptations(&self, text: &str, phonemes: Vec<Phoneme>) -> Result<Vec<Phoneme>> {
523 let mut adapted_phonemes = phonemes;
524
525 let key = text.to_lowercase();
527 if let Some(adaptation) = self.adaptation_model.word_adaptations.get(&key) {
528 if adaptation.support_count >= self.min_corrections_threshold {
529 adapted_phonemes = adaptation
531 .target_phonemes
532 .iter()
533 .map(|symbol| Phoneme::new(symbol.clone()))
534 .collect();
535 }
536 }
537
538 Ok(adapted_phonemes)
539 }
540
541 pub fn get_statistics(&self) -> AdaptationStats {
543 self.adaptation_model.stats.clone()
544 }
545}
546
547impl Default for MultilingualPhonemeMapper {
548 fn default() -> Self {
549 Self::new()
550 }
551}
552
553impl MultilingualPhonemeMapper {
555 pub fn new() -> Self {
557 let mut mapper = Self {
558 cross_lang_mappings: HashMap::new(),
559 universal_phonemes: UniversalPhonemeInventory::default(),
560 language_systems: HashMap::new(),
561 };
562
563 mapper.initialize_language_systems();
565
566 mapper
567 }
568
569 fn initialize_language_systems(&mut self) {
571 let english_system = LanguagePhonemeSystem {
573 language: LanguageCode::EnUs,
574 native_phonemes: vec![
575 "æ".to_string(),
576 "ɑ".to_string(),
577 "ɔ".to_string(),
578 "ɛ".to_string(),
579 "ɪ".to_string(),
580 "ʊ".to_string(),
581 "ʌ".to_string(),
582 "ə".to_string(),
583 "i".to_string(),
584 "u".to_string(),
585 "eɪ".to_string(),
586 "oʊ".to_string(),
587 "aɪ".to_string(),
588 "aʊ".to_string(),
589 "ɔɪ".to_string(),
590 ],
591 allophones: HashMap::new(),
592 phonotactic_rules: Vec::new(),
593 stress_patterns: StressPatternSystem::default(),
594 };
595 self.language_systems
596 .insert(LanguageCode::EnUs, english_system);
597
598 }
600
601 pub fn map_phonemes(
603 &self,
604 phonemes: &[Phoneme],
605 source_lang: LanguageCode,
606 target_lang: LanguageCode,
607 ) -> Result<Vec<Phoneme>> {
608 let mapping_key = (source_lang, target_lang);
609
610 if let Some(mapping) = self.cross_lang_mappings.get(&mapping_key) {
611 let mapped_phonemes = phonemes
612 .iter()
613 .map(|phoneme| self.map_single_phoneme(phoneme, mapping))
614 .collect();
615
616 Ok(mapped_phonemes)
617 } else {
618 self.map_via_universal(phonemes, source_lang, target_lang)
620 }
621 }
622
623 fn map_single_phoneme(&self, phoneme: &Phoneme, mapping: &PhonemeMapping) -> Phoneme {
625 if let Some(mapped_symbol) = mapping.direct_mappings.get(&phoneme.symbol) {
627 let mut mapped_phoneme = phoneme.clone();
628 mapped_phoneme.symbol = mapped_symbol.clone();
629 return mapped_phoneme;
630 }
631
632 if let Some(approximate) = mapping.approximate_mappings.get(&phoneme.symbol) {
634 if let Some((mapped_symbol, _confidence)) = approximate.first() {
635 let mut mapped_phoneme = phoneme.clone();
636 mapped_phoneme.symbol = mapped_symbol.clone();
637 return mapped_phoneme;
638 }
639 }
640
641 phoneme.clone()
643 }
644
645 fn map_via_universal(
647 &self,
648 phonemes: &[Phoneme],
649 _source_lang: LanguageCode,
650 _target_lang: LanguageCode,
651 ) -> Result<Vec<Phoneme>> {
652 Ok(phonemes.to_vec())
654 }
655}
656
657impl Default for StressPatternSystem {
658 fn default() -> Self {
659 Self {
660 default_pattern: vec![1, 0],
661 word_rules: HashMap::new(),
662 pattern_rules: Vec::new(),
663 }
664 }
665}
666
667impl Default for PhonemeQualityScorer {
668 fn default() -> Self {
669 Self::new()
670 }
671}
672
673impl PhonemeQualityScorer {
675 pub fn new() -> Self {
677 Self {
678 quality_models: HashMap::new(),
679 global_factors: GlobalQualityFactors::default(),
680 assessment_history: VecDeque::new(),
681 }
682 }
683
684 pub fn assess_quality(
686 &mut self,
687 text: &str,
688 phonemes: &[Phoneme],
689 language: LanguageCode,
690 ) -> QualityAssessment {
691 let overall_score = self.calculate_overall_quality(phonemes, language);
692 let phoneme_scores = self.calculate_phoneme_scores(phonemes, language);
693 let factor_breakdown = self.calculate_factor_breakdown(phonemes, language);
694
695 let assessment = QualityAssessment {
696 text: text.to_string(),
697 phonemes: phonemes.to_vec(),
698 overall_score,
699 phoneme_scores,
700 factor_breakdown,
701 timestamp: SystemTime::now(),
702 };
703
704 self.assessment_history.push_back(assessment.clone());
706 if self.assessment_history.len() > 1000 {
707 self.assessment_history.pop_front();
708 }
709
710 assessment
711 }
712
713 fn calculate_overall_quality(&self, phonemes: &[Phoneme], _language: LanguageCode) -> f32 {
715 if phonemes.is_empty() {
716 return 0.0;
717 }
718
719 let total_confidence: f32 = phonemes.iter().map(|p| p.confidence).sum();
720 total_confidence / phonemes.len() as f32
721 }
722
723 fn calculate_phoneme_scores(&self, phonemes: &[Phoneme], _language: LanguageCode) -> Vec<f32> {
725 phonemes.iter().map(|p| p.confidence).collect()
726 }
727
728 fn calculate_factor_breakdown(
730 &self,
731 phonemes: &[Phoneme],
732 _language: LanguageCode,
733 ) -> HashMap<String, f32> {
734 let mut breakdown = HashMap::new();
735
736 breakdown.insert(
737 "confidence".to_string(),
738 phonemes.iter().map(|p| p.confidence).sum::<f32>() / phonemes.len() as f32,
739 );
740 breakdown.insert("naturalness".to_string(), 0.8);
741 breakdown.insert("distinctiveness".to_string(), 0.85);
742 breakdown.insert("acoustic_clarity".to_string(), 0.9);
743
744 breakdown
745 }
746}
747
748impl StreamingG2pProcessor {
750 pub fn new(
752 language: LanguageCode,
753 phoneme_sender: mpsc::UnboundedSender<StreamingPhoneme>,
754 ) -> Self {
755 Self {
756 language,
757 text_buffer: Arc::new(Mutex::new(String::new())),
758 phoneme_sender,
759 config: StreamingConfig::default(),
760 stats: Arc::new(Mutex::new(StreamingStats::default())),
761 }
762 }
763
764 pub fn add_text(&self, text: &str) -> Result<()> {
766 let mut buffer = self
767 .text_buffer
768 .lock()
769 .expect("lock should not be poisoned");
770 buffer.push_str(text);
771
772 let mut stats = self.stats.lock().expect("lock should not be poisoned");
774 stats.total_chars_processed += text.len();
775
776 Ok(())
777 }
778
779 pub async fn process_buffer(&self) -> Result<()> {
781 let start_time = Instant::now();
782
783 let text_to_process = {
784 let mut buffer = self
785 .text_buffer
786 .lock()
787 .expect("lock should not be poisoned");
788 let to_process = buffer.clone();
789 buffer.clear();
790 to_process
791 };
792
793 if text_to_process.is_empty() {
794 return Ok(());
795 }
796
797 let mut time_offset = 0.0f32;
799
800 for word in text_to_process.split_whitespace() {
801 for (i, char) in word.chars().enumerate() {
803 let phoneme = Phoneme::new(char.to_string());
804 let duration = 100.0; let streaming_phoneme = StreamingPhoneme {
807 phoneme,
808 start_time_ms: time_offset,
809 duration_ms: duration,
810 streaming_confidence: 0.8,
811 is_word_boundary: i == word.len() - 1,
812 };
813
814 if self.phoneme_sender.send(streaming_phoneme).is_err() {
815 break; }
817
818 time_offset += duration;
819 }
820 }
821
822 let processing_time = start_time.elapsed().as_millis() as f32;
824 let mut stats = self.stats.lock().expect("lock should not be poisoned");
825 stats.avg_latency_ms = (stats.avg_latency_ms + processing_time) / 2.0;
826 if processing_time > stats.peak_latency_ms {
827 stats.peak_latency_ms = processing_time;
828 }
829
830 Ok(())
831 }
832
833 pub fn get_statistics(&self) -> StreamingStats {
835 self.stats
836 .lock()
837 .expect("lock should not be poisoned")
838 .clone()
839 }
840}
841
842impl Default for StreamingConfig {
843 fn default() -> Self {
844 Self {
845 buffer_size: 1024,
846 chunk_size: 64,
847 lookahead_size: 16,
848 min_latency_ms: 10.0,
849 max_latency_ms: 100.0,
850 enable_adaptation: true,
851 }
852 }
853}
854
855impl EmotionAwareG2pProcessor {
857 pub fn new(base_processor: Arc<dyn crate::G2p>) -> Self {
859 Self {
860 base_processor,
861 emotion_classifier: EmotionClassifier::default(),
862 emotion_modifications: HashMap::new(),
863 processing_history: VecDeque::new(),
864 }
865 }
866
867 pub async fn process_with_emotion(
869 &mut self,
870 text: &str,
871 language: Option<LanguageCode>,
872 ) -> Result<Vec<Phoneme>> {
873 let (detected_emotion, emotion_confidence) = self.classify_emotion(text);
875
876 let base_phonemes = self.base_processor.to_phonemes(text, language).await?;
878
879 let modified_phonemes = self.apply_emotion_modifications(&base_phonemes, &detected_emotion);
881
882 let entry = EmotionProcessingEntry {
884 text: text.to_string(),
885 detected_emotion: detected_emotion.clone(),
886 emotion_confidence,
887 original_phonemes: base_phonemes,
888 modified_phonemes: modified_phonemes.clone(),
889 timestamp: SystemTime::now(),
890 };
891
892 self.processing_history.push_back(entry);
893 if self.processing_history.len() > 1000 {
894 self.processing_history.pop_front();
895 }
896
897 Ok(modified_phonemes)
898 }
899
900 fn classify_emotion(&self, text: &str) -> (EmotionType, f32) {
902 let mut emotion_scores = HashMap::new();
903
904 for emotion_type in [
906 EmotionType::Neutral,
907 EmotionType::Happy,
908 EmotionType::Sad,
909 EmotionType::Angry,
910 EmotionType::Fearful,
911 EmotionType::Surprised,
912 EmotionType::Disgusted,
913 EmotionType::Excited,
914 EmotionType::Calm,
915 ] {
916 emotion_scores.insert(emotion_type, 0.0f32);
917 }
918
919 let words: Vec<&str> = text.split_whitespace().collect();
921 for word in words {
922 let word_lower = word
923 .to_lowercase()
924 .trim_matches(|c: char| !c.is_alphabetic())
925 .to_string();
926
927 match word_lower.as_str() {
929 "happy" | "joy" | "glad" | "wonderful" | "very" => {
930 *emotion_scores
931 .get_mut(&EmotionType::Happy)
932 .expect("key was pre-initialized") += 1.0;
933 }
934 "sad" | "unhappy" | "depressed" => {
935 *emotion_scores
936 .get_mut(&EmotionType::Sad)
937 .expect("key was pre-initialized") += 1.0;
938 }
939 "angry" | "mad" | "furious" | "hate" => {
940 *emotion_scores
941 .get_mut(&EmotionType::Angry)
942 .expect("key was pre-initialized") += 2.0;
943 }
945 "terrible" => {
946 *emotion_scores
947 .get_mut(&EmotionType::Sad)
948 .expect("key was pre-initialized") += 0.5; }
950 "excited" | "amazing" | "fantastic" | "awesome" => {
951 *emotion_scores
952 .get_mut(&EmotionType::Excited)
953 .expect("key was pre-initialized") += 1.0;
954 }
955 _ => {
956 *emotion_scores
957 .get_mut(&EmotionType::Neutral)
958 .expect("key was pre-initialized") += 0.1;
959 }
960 }
961 }
962
963 let emotion_priority = [
965 EmotionType::Angry,
966 EmotionType::Excited,
967 EmotionType::Happy,
968 EmotionType::Sad,
969 EmotionType::Fearful,
970 EmotionType::Surprised,
971 EmotionType::Disgusted,
972 EmotionType::Calm,
973 EmotionType::Neutral,
974 ];
975
976 let mut best_emotion = EmotionType::Neutral;
977 let mut best_score = 0.0;
978
979 for emotion in emotion_priority {
980 if let Some(&score) = emotion_scores.get(&emotion) {
981 if score > best_score {
982 best_score = score;
983 best_emotion = emotion;
984 }
985 }
986 }
987
988 let confidence = if best_score > 0.0 { 0.8 } else { 0.5 };
989
990 (best_emotion, confidence)
991 }
992
993 fn apply_emotion_modifications(
995 &self,
996 phonemes: &[Phoneme],
997 emotion: &EmotionType,
998 ) -> Vec<Phoneme> {
999 if let Some(modification) = self.emotion_modifications.get(emotion) {
1000 phonemes
1001 .iter()
1002 .map(|phoneme| {
1003 let mut modified_phoneme = phoneme.clone();
1004
1005 if let Some(duration_mod) =
1007 modification.duration_modifications.get(&phoneme.symbol)
1008 {
1009 if let Some(original_duration) = modified_phoneme.duration_ms {
1010 modified_phoneme.duration_ms = Some(original_duration * duration_mod);
1011 }
1012 }
1013
1014 if let Some(stress_mod) = modification.stress_modifications.get(&phoneme.symbol)
1016 {
1017 modified_phoneme.stress = *stress_mod;
1018 }
1019
1020 modified_phoneme
1021 })
1022 .collect()
1023 } else {
1024 phonemes.to_vec()
1025 }
1026 }
1027
1028 pub fn get_processing_history(&self) -> Vec<EmotionProcessingEntry> {
1030 self.processing_history.iter().cloned().collect()
1031 }
1032}
1033
1034impl Default for VoiceQualityAdjustments {
1035 fn default() -> Self {
1036 Self {
1037 breathiness: 0.0,
1038 creakiness: 0.0,
1039 tenseness: 0.0,
1040 nasality: 0.0,
1041 }
1042 }
1043}
1044
1045#[cfg(test)]
1046mod tests {
1047 use super::*;
1048
1049 #[test]
1050 fn test_adaptive_pronunciation_system() {
1051 let mut system = AdaptivePronunciationSystem::new(LanguageCode::EnUs);
1052
1053 let correction = UserCorrection {
1054 text: "tomato".to_string(),
1055 original_phonemes: vec![Phoneme::new("təˈmeɪtoʊ")],
1056 corrected_phonemes: vec![Phoneme::new("təˈmɑːtoʊ")],
1057 timestamp: SystemTime::now(),
1058 context: None,
1059 user_confidence: 0.9,
1060 };
1061
1062 assert!(system.add_correction(correction).is_ok());
1063 assert_eq!(system.adaptation_model.word_adaptations.len(), 1);
1064 }
1065
1066 #[test]
1067 fn test_multilingual_phoneme_mapper() {
1068 let mapper = MultilingualPhonemeMapper::new();
1069
1070 let phonemes = vec![Phoneme::new("æ")];
1071 let result = mapper.map_phonemes(&phonemes, LanguageCode::EnUs, LanguageCode::De);
1072
1073 assert!(result.is_ok());
1074 }
1075
1076 #[test]
1077 fn test_phoneme_quality_scorer() {
1078 let mut scorer = PhonemeQualityScorer::new();
1079
1080 let phonemes = vec![
1081 Phoneme::with_confidence("h", 0.9),
1082 Phoneme::with_confidence("ɛ", 0.8),
1083 Phoneme::with_confidence("l", 0.85),
1084 ];
1085
1086 let assessment = scorer.assess_quality("hello", &phonemes, LanguageCode::EnUs);
1087
1088 assert!(assessment.overall_score > 0.0);
1089 assert_eq!(assessment.phoneme_scores.len(), 3);
1090 }
1091
1092 #[tokio::test]
1093 async fn test_streaming_g2p_processor() {
1094 let (sender, mut receiver) = mpsc::unbounded_channel();
1095 let processor = StreamingG2pProcessor::new(LanguageCode::EnUs, sender);
1096
1097 assert!(processor.add_text("hello").is_ok());
1098 assert!(processor.process_buffer().await.is_ok());
1099
1100 if let Ok(streaming_phoneme) = receiver.try_recv() {
1102 assert!(!streaming_phoneme.phoneme.symbol.is_empty());
1103 }
1104 }
1105
1106 #[tokio::test]
1107 async fn test_emotion_aware_processor() {
1108 let dummy_processor = Arc::new(crate::DummyG2p::new());
1109 let mut emotion_processor = EmotionAwareG2pProcessor::new(dummy_processor);
1110
1111 let result = emotion_processor
1112 .process_with_emotion("I am very happy!", Some(LanguageCode::EnUs))
1113 .await;
1114
1115 assert!(result.is_ok());
1116 assert_eq!(emotion_processor.processing_history.len(), 1);
1117
1118 let entry = &emotion_processor.processing_history[0];
1119 assert_eq!(entry.detected_emotion, EmotionType::Happy);
1121 }
1122
1123 #[test]
1124 fn test_emotion_classification() {
1125 let dummy_processor = Arc::new(crate::DummyG2p::new());
1126 let emotion_processor = EmotionAwareG2pProcessor::new(dummy_processor);
1127
1128 let (emotion, confidence) =
1129 emotion_processor.classify_emotion("I am so excited about this!");
1130 assert_eq!(emotion, EmotionType::Excited);
1131 assert!(confidence > 0.0);
1132
1133 let (emotion, _) =
1134 emotion_processor.classify_emotion("This is terrible and makes me angry!");
1135 assert_eq!(emotion, EmotionType::Angry);
1136 }
1137}