1use crate::prelude::*;
50use serde::{Deserialize, Serialize};
51use std::collections::HashMap;
52use std::sync::atomic::{AtomicBool, AtomicU32, AtomicU64, Ordering};
53use std::sync::Arc;
54use std::time::{Duration, Instant};
55use tokio::sync::{Mutex, RwLock};
56
57#[derive(Debug, Clone, Serialize, Deserialize)]
59pub struct MobileConversionConfig {
60 pub enable_neon: bool,
62 pub enable_power_management: bool,
64 pub enable_thermal_management: bool,
66 pub enable_memory_optimization: bool,
68 pub target_memory_mb: f64,
70 pub max_cpu_temperature: f64,
72 pub min_battery_percent: f64,
74 pub enable_adaptive_quality: bool,
76 pub max_concurrent_conversions: usize,
78 pub mobile_buffer_size: usize,
80 pub use_arm_optimized_algorithms: bool,
82}
83
84impl Default for MobileConversionConfig {
85 fn default() -> Self {
86 Self {
87 enable_neon: cfg!(target_arch = "aarch64"),
88 enable_power_management: true,
89 enable_thermal_management: true,
90 enable_memory_optimization: true,
91 target_memory_mb: 50.0,
92 max_cpu_temperature: 75.0,
93 min_battery_percent: 15.0,
94 enable_adaptive_quality: true,
95 max_concurrent_conversions: 2, mobile_buffer_size: 1024,
97 use_arm_optimized_algorithms: cfg!(target_arch = "aarch64"),
98 }
99 }
100}
101
102#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
104pub enum PowerMode {
105 HighPerformance,
107 Balanced,
109 PowerSaver,
111 UltraPowerSaver,
113}
114
115#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
117pub enum MobilePlatform {
118 IOS,
120 Android,
122 GenericARM,
124 Desktop,
126}
127
128impl MobilePlatform {
129 pub fn detect() -> Self {
131 #[cfg(target_os = "ios")]
132 return Self::IOS;
133
134 #[cfg(target_os = "android")]
135 return Self::Android;
136
137 #[cfg(all(
138 target_arch = "aarch64",
139 not(any(target_os = "ios", target_os = "android"))
140 ))]
141 return Self::GenericARM;
142
143 #[cfg(not(any(
144 target_os = "ios",
145 target_os = "android",
146 all(
147 target_arch = "aarch64",
148 not(any(target_os = "ios", target_os = "android"))
149 )
150 )))]
151 Self::Desktop
152 }
153
154 pub fn is_mobile(&self) -> bool {
156 matches!(self, Self::IOS | Self::Android | Self::GenericARM)
157 }
158}
159
160#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
162pub enum ThermalState {
163 Normal,
165 Warm,
167 Hot,
169 Critical,
171}
172
173#[derive(Debug, Clone, Serialize, Deserialize)]
175pub struct MobileDeviceInfo {
176 pub platform: MobilePlatform,
178 pub device_model: String,
180 pub cpu_architecture: String,
182 pub cpu_cores: usize,
184 pub ram_mb: u64,
186 pub neon_supported: bool,
188 pub battery_percent: f64,
190 pub cpu_temperature: f64,
192 pub thermal_state: ThermalState,
194 pub available_storage_mb: u64,
196}
197
198impl MobileDeviceInfo {
199 pub fn detect() -> Self {
201 let platform = MobilePlatform::detect();
202 let cpu_cores = num_cpus::get();
203 let neon_supported = Self::detect_neon_support();
204 let battery_percent = Self::detect_battery_level();
205 let cpu_temperature = Self::detect_cpu_temperature();
206 let thermal_state = Self::temperature_to_thermal_state(cpu_temperature);
207
208 Self {
209 platform,
210 device_model: Self::detect_device_model(),
211 cpu_architecture: std::env::consts::ARCH.to_string(),
212 cpu_cores,
213 ram_mb: Self::detect_ram_mb(),
214 neon_supported,
215 battery_percent,
216 cpu_temperature,
217 thermal_state,
218 available_storage_mb: Self::detect_available_storage(),
219 }
220 }
221
222 fn temperature_to_thermal_state(temperature: f64) -> ThermalState {
224 match temperature {
225 t if t < 60.0 => ThermalState::Normal,
226 t if t < 70.0 => ThermalState::Warm,
227 t if t < 80.0 => ThermalState::Hot,
228 _ => ThermalState::Critical,
229 }
230 }
231
232 pub fn recommend_power_mode(&self) -> PowerMode {
234 match (self.battery_percent, self.thermal_state) {
235 (_, ThermalState::Critical) => PowerMode::UltraPowerSaver,
236 (b, ThermalState::Hot) if b < 30.0 => PowerMode::UltraPowerSaver,
237 (b, _) if b < 10.0 => PowerMode::UltraPowerSaver,
238 (b, _) if b < 25.0 => PowerMode::PowerSaver,
239 (b, _) if b < 50.0 => PowerMode::Balanced,
240 _ => PowerMode::HighPerformance,
241 }
242 }
243
244 fn detect_device_model() -> String {
247 match MobilePlatform::detect() {
248 MobilePlatform::IOS => "iPhone/iPad".to_string(),
249 MobilePlatform::Android => "Android Device".to_string(),
250 MobilePlatform::GenericARM => "ARM Device".to_string(),
251 MobilePlatform::Desktop => "Desktop".to_string(),
252 }
253 }
254
255 fn detect_ram_mb() -> u64 {
256 match std::env::var("DEVICE_RAM_MB") {
258 Ok(ram) => ram.parse().unwrap_or(4096),
259 Err(_) => match MobilePlatform::detect() {
260 MobilePlatform::IOS => 6144, MobilePlatform::Android => 8192, _ => 4096,
263 },
264 }
265 }
266
267 fn detect_neon_support() -> bool {
268 cfg!(target_arch = "aarch64")
269 || (cfg!(target_arch = "arm") && cfg!(target_feature = "neon"))
270 }
271
272 fn detect_battery_level() -> f64 {
273 match std::env::var("BATTERY_LEVEL") {
274 Ok(level) => level.parse().unwrap_or(75.0),
275 Err(_) => 75.0,
276 }
277 }
278
279 fn detect_cpu_temperature() -> f64 {
280 match std::env::var("CPU_TEMPERATURE") {
281 Ok(temp) => temp.parse().unwrap_or(55.0),
282 Err(_) => 55.0,
283 }
284 }
285
286 fn detect_available_storage() -> u64 {
287 match std::env::var("AVAILABLE_STORAGE_MB") {
288 Ok(storage) => storage.parse().unwrap_or(10240),
289 Err(_) => 10240, }
291 }
292}
293
294pub struct MobileVoiceConverter {
296 converter: Arc<VoiceConverter>,
298 config: MobileConversionConfig,
300 device_info: Arc<RwLock<MobileDeviceInfo>>,
302 power_mode: Arc<RwLock<PowerMode>>,
304 stats: Arc<MobileConversionStats>,
306 neon_optimizer: Option<Arc<NeonOptimizer>>,
308 conversion_semaphore: Arc<tokio::sync::Semaphore>,
310 thermal_monitoring: Arc<AtomicBool>,
312}
313
314impl MobileVoiceConverter {
315 pub async fn new() -> Result<Self> {
317 Self::with_config(MobileConversionConfig::default()).await
318 }
319
320 pub async fn with_config(config: MobileConversionConfig) -> Result<Self> {
322 let converter = Arc::new(VoiceConverter::new()?);
323 let device_info = Arc::new(RwLock::new(MobileDeviceInfo::detect()));
324 let recommended_power = device_info.read().await.recommend_power_mode();
325
326 let neon_optimizer = if config.enable_neon && device_info.read().await.neon_supported {
327 Some(Arc::new(NeonOptimizer::new()))
328 } else {
329 None
330 };
331
332 Ok(Self {
333 converter,
334 config: config.clone(),
335 device_info,
336 power_mode: Arc::new(RwLock::new(recommended_power)),
337 stats: Arc::new(MobileConversionStats::new()),
338 neon_optimizer,
339 conversion_semaphore: Arc::new(tokio::sync::Semaphore::new(
340 config.max_concurrent_conversions,
341 )),
342 thermal_monitoring: Arc::new(AtomicBool::new(config.enable_thermal_management)),
343 })
344 }
345
346 pub async fn set_power_mode(&self, mode: PowerMode) -> Result<()> {
348 *self.power_mode.write().await = mode;
349 self.stats.record_power_mode_change(mode);
350
351 self.apply_power_mode_settings(mode).await?;
353
354 Ok(())
355 }
356
357 pub async fn get_power_mode(&self) -> PowerMode {
359 *self.power_mode.read().await
360 }
361
362 pub async fn convert_mobile_optimized(
364 &self,
365 request: &ConversionRequest,
366 ) -> Result<ConversionResult> {
367 let start_time = Instant::now();
368
369 let _permit = self
371 .conversion_semaphore
372 .acquire()
373 .await
374 .map_err(|e| Error::runtime(format!("Failed to acquire conversion permit: {e}")))?;
375
376 if self.config.enable_thermal_management {
378 self.check_thermal_state().await?;
379 }
380
381 self.update_device_info().await?;
383
384 let processing_quality = self.determine_processing_quality().await;
386
387 let result = self
389 .perform_optimized_conversion(request, processing_quality)
390 .await?;
391
392 let processing_time = start_time.elapsed();
394 self.stats
395 .record_conversion(processing_time, request.conversion_type.clone());
396
397 Ok(result)
398 }
399
400 pub async fn convert_batch_mobile(
402 &self,
403 requests: &[ConversionRequest],
404 ) -> Result<Vec<ConversionResult>> {
405 let mut results = Vec::with_capacity(requests.len());
406
407 let chunk_size = match self.get_power_mode().await {
409 PowerMode::HighPerformance => 4,
410 PowerMode::Balanced => 2,
411 PowerMode::PowerSaver => 1,
412 PowerMode::UltraPowerSaver => 1,
413 };
414
415 for chunk in requests.chunks(chunk_size) {
416 let mut chunk_results = Vec::new();
417
418 if chunk_size > 1 && self.device_info.read().await.cpu_cores > 2 {
420 let mut handles = Vec::new();
422
423 for request in chunk {
424 let self_clone = self.clone_converter_for_concurrent_use().await?;
425 let request_clone = request.clone();
426 let handle = tokio::spawn(async move {
427 self_clone.convert_mobile_optimized(&request_clone).await
428 });
429 handles.push(handle);
430 }
431
432 for handle in handles {
433 let result = handle.await.map_err(|e| {
434 Error::runtime(format!("Concurrent conversion failed: {e}"))
435 })??;
436 chunk_results.push(result);
437 }
438 } else {
439 for request in chunk {
441 let result = self.convert_mobile_optimized(request).await?;
442 chunk_results.push(result);
443 }
444 }
445
446 results.extend(chunk_results);
447
448 if self.device_info.read().await.thermal_state == ThermalState::Hot {
450 tokio::time::sleep(Duration::from_millis(100)).await;
451 }
452 }
453
454 Ok(results)
455 }
456
457 pub async fn update_device_info(&self) -> Result<()> {
459 let new_info = MobileDeviceInfo::detect();
460 let old_thermal_state = self.device_info.read().await.thermal_state;
461
462 *self.device_info.write().await = new_info.clone();
463
464 if new_info.thermal_state != old_thermal_state {
466 match new_info.thermal_state {
467 ThermalState::Critical => {
468 self.set_power_mode(PowerMode::UltraPowerSaver).await?;
469 }
470 ThermalState::Hot => {
471 let current_power = self.get_power_mode().await;
472 if current_power == PowerMode::HighPerformance {
473 self.set_power_mode(PowerMode::PowerSaver).await?;
474 }
475 }
476 _ => {
477 let recommended = new_info.recommend_power_mode();
479 let current = self.get_power_mode().await;
480 if recommended != current && new_info.battery_percent > 30.0 {
481 self.set_power_mode(recommended).await?;
482 }
483 }
484 }
485 }
486
487 self.stats
488 .record_device_update(new_info.thermal_state, new_info.battery_percent);
489
490 Ok(())
491 }
492
493 pub fn get_statistics(&self) -> MobileConversionStatistics {
495 self.stats.get_statistics()
496 }
497
498 pub async fn start_device_monitoring(&self) -> Result<tokio::task::JoinHandle<()>> {
500 let device_info = self.device_info.clone();
501 let thermal_monitoring = self.thermal_monitoring.clone();
502 let stats = self.stats.clone();
503 let power_mode = self.power_mode.clone();
504
505 let handle = tokio::spawn(async move {
506 let mut interval = tokio::time::interval(Duration::from_secs(10));
507
508 loop {
509 interval.tick().await;
510
511 if thermal_monitoring.load(Ordering::Relaxed) {
512 let new_info = MobileDeviceInfo::detect();
513 let old_thermal_state = device_info.read().await.thermal_state;
514
515 *device_info.write().await = new_info.clone();
516
517 if new_info.thermal_state != old_thermal_state {
519 stats.record_thermal_event(new_info.thermal_state);
520 }
521
522 let recommended = new_info.recommend_power_mode();
524 let current = *power_mode.read().await;
525
526 if recommended != current
527 && (new_info.thermal_state == ThermalState::Critical
528 || new_info.battery_percent < 10.0)
529 {
530 *power_mode.write().await = recommended;
531 stats.record_power_mode_change(recommended);
532 }
533 }
534 }
535 });
536
537 Ok(handle)
538 }
539
540 async fn apply_power_mode_settings(&self, mode: PowerMode) -> Result<()> {
543 match mode {
545 PowerMode::HighPerformance => {
546 }
548 PowerMode::Balanced => {
549 }
551 PowerMode::PowerSaver => {
552 }
554 PowerMode::UltraPowerSaver => {
555 }
557 }
558
559 Ok(())
560 }
561
562 async fn check_thermal_state(&self) -> Result<()> {
563 let thermal_state = self.device_info.read().await.thermal_state;
564
565 match thermal_state {
566 ThermalState::Critical => {
567 self.set_power_mode(PowerMode::UltraPowerSaver).await?;
569 tokio::time::sleep(Duration::from_millis(500)).await;
571 }
572 ThermalState::Hot => {
573 let current_mode = self.get_power_mode().await;
575 if current_mode == PowerMode::HighPerformance {
576 self.set_power_mode(PowerMode::PowerSaver).await?;
577 }
578 tokio::time::sleep(Duration::from_millis(100)).await;
580 }
581 _ => {
582 }
584 }
585
586 Ok(())
587 }
588
589 async fn determine_processing_quality(&self) -> ProcessingQuality {
590 let power_mode = self.get_power_mode().await;
591 let device_info = self.device_info.read().await;
592
593 match (
594 power_mode,
595 device_info.thermal_state,
596 device_info.battery_percent,
597 ) {
598 (PowerMode::UltraPowerSaver, _, _) => ProcessingQuality::Minimal,
599 (_, ThermalState::Critical, _) => ProcessingQuality::Minimal,
600 (PowerMode::PowerSaver, _, _) => ProcessingQuality::Reduced,
601 (_, ThermalState::Hot, _) => ProcessingQuality::Reduced,
602 (_, _, battery) if battery < 15.0 => ProcessingQuality::Reduced,
603 (PowerMode::HighPerformance, ThermalState::Normal, battery) if battery > 50.0 => {
604 ProcessingQuality::High
605 }
606 _ => ProcessingQuality::Standard,
607 }
608 }
609
610 async fn perform_optimized_conversion(
611 &self,
612 request: &ConversionRequest,
613 quality: ProcessingQuality,
614 ) -> Result<ConversionResult> {
615 let start_time = Instant::now();
616
617 let optimized_request = self.optimize_request_for_mobile(request, quality).await?;
619
620 let result = if let Some(neon_optimizer) = &self.neon_optimizer {
622 self.convert_with_neon(&optimized_request, neon_optimizer, quality)
623 .await?
624 } else {
625 self.convert_standard(&optimized_request, quality).await?
626 };
627
628 let optimized_result = self.optimize_result_for_mobile(result, quality).await?;
630
631 let processing_time = start_time.elapsed();
632
633 Ok(ConversionResult {
635 processing_time,
636 ..optimized_result
637 })
638 }
639
640 async fn optimize_request_for_mobile(
641 &self,
642 request: &ConversionRequest,
643 quality: ProcessingQuality,
644 ) -> Result<ConversionRequest> {
645 let mut optimized = request.clone();
646
647 if optimized.source_audio.len() > self.config.mobile_buffer_size * 4 {
649 optimized
651 .source_audio
652 .truncate(self.config.mobile_buffer_size * 4);
653 }
654
655 match quality {
657 ProcessingQuality::Minimal => {
658 optimized.source_sample_rate = optimized.source_sample_rate.min(22050)
659 }
660 ProcessingQuality::Reduced => {
661 optimized.source_sample_rate = optimized.source_sample_rate.min(32000)
662 }
663 _ => {} }
665
666 Ok(optimized)
667 }
668
669 async fn convert_with_neon(
670 &self,
671 request: &ConversionRequest,
672 neon_optimizer: &NeonOptimizer,
673 quality: ProcessingQuality,
674 ) -> Result<ConversionResult> {
675 let mut audio_data = request.source_audio.clone();
677
678 neon_optimizer.preprocess_audio(&mut audio_data, quality);
680
681 let converted_audio =
683 neon_optimizer.convert_audio(&audio_data, &request.conversion_type, quality)?;
684
685 let final_audio = neon_optimizer.postprocess_audio(converted_audio, quality);
687
688 Ok(ConversionResult {
689 request_id: request.id.clone(),
690 converted_audio: final_audio,
691 output_sample_rate: request.source_sample_rate,
692 quality_metrics: HashMap::new(),
693 artifacts: None,
694 objective_quality: Some(crate::types::ObjectiveQualityMetrics {
695 overall_score: quality.as_score() as f32,
696 spectral_similarity: 0.85,
697 temporal_consistency: 0.88,
698 prosodic_preservation: 0.82,
699 naturalness: quality.as_score() as f32,
700 perceptual_quality: quality.as_score() as f32,
701 snr_estimate: 25.0,
702 segmental_snr: 22.0,
703 }),
704 processing_time: Duration::from_millis(0), conversion_type: request.conversion_type.clone(),
706 success: true,
707 error_message: None,
708 timestamp: std::time::SystemTime::now(),
709 })
710 }
711
712 async fn convert_standard(
713 &self,
714 request: &ConversionRequest,
715 quality: ProcessingQuality,
716 ) -> Result<ConversionResult> {
717 let result = self.converter.convert(request.clone()).await?;
719
720 let mut optimized_result = result;
722
723 match quality {
724 ProcessingQuality::Minimal => {
725 optimized_result.converted_audio =
727 self.apply_audio_compression(&optimized_result.converted_audio, 0.5);
728 }
729 ProcessingQuality::Reduced => {
730 optimized_result.converted_audio =
732 self.apply_audio_compression(&optimized_result.converted_audio, 0.7);
733 }
734 _ => {} }
736
737 Ok(optimized_result)
738 }
739
740 async fn optimize_result_for_mobile(
741 &self,
742 mut result: ConversionResult,
743 quality: ProcessingQuality,
744 ) -> Result<ConversionResult> {
745 match quality {
747 ProcessingQuality::Minimal | ProcessingQuality::Reduced => {
748 result.quality_metrics.clear();
750 result.artifacts = None;
751 }
752 _ => {}
753 }
754
755 Ok(result)
756 }
757
758 fn apply_audio_compression(&self, audio: &[f32], ratio: f32) -> Vec<f32> {
759 audio.iter().map(|&sample| sample * ratio).collect()
761 }
762
763 async fn clone_converter_for_concurrent_use(&self) -> Result<Self> {
764 Ok(Self {
766 converter: Arc::clone(&self.converter),
767 config: self.config.clone(),
768 device_info: Arc::clone(&self.device_info),
769 power_mode: Arc::clone(&self.power_mode),
770 stats: Arc::clone(&self.stats),
771 neon_optimizer: self.neon_optimizer.clone(),
772 conversion_semaphore: Arc::clone(&self.conversion_semaphore),
773 thermal_monitoring: Arc::clone(&self.thermal_monitoring),
774 })
775 }
776}
777
778#[derive(Debug, Clone, Copy, PartialEq, Eq)]
780pub enum ProcessingQuality {
781 Minimal,
783 Reduced,
785 Standard,
787 High,
789}
790
791impl ProcessingQuality {
792 fn as_score(&self) -> f64 {
793 match self {
794 ProcessingQuality::Minimal => 0.4,
795 ProcessingQuality::Reduced => 0.6,
796 ProcessingQuality::Standard => 0.8,
797 ProcessingQuality::High => 1.0,
798 }
799 }
800}
801
802pub struct NeonOptimizer {
804 enabled: bool,
805}
806
807impl Default for NeonOptimizer {
808 fn default() -> Self {
809 Self::new()
810 }
811}
812
813impl NeonOptimizer {
814 pub fn new() -> Self {
816 Self {
817 enabled: cfg!(target_arch = "aarch64"),
818 }
819 }
820
821 pub fn is_available() -> bool {
823 cfg!(target_arch = "aarch64")
824 || (cfg!(target_arch = "arm") && cfg!(target_feature = "neon"))
825 }
826
827 pub fn preprocess_audio(&self, audio_data: &mut [f32], _quality: ProcessingQuality) {
829 if !self.enabled {
830 return;
831 }
832
833 #[cfg(target_arch = "aarch64")]
836 {
837 self.neon_normalize_audio(audio_data);
838 }
839
840 #[cfg(not(target_arch = "aarch64"))]
841 {
842 for sample in audio_data.iter_mut() {
844 *sample = sample.clamp(-1.0, 1.0);
845 }
846 }
847 }
848
849 pub fn convert_audio(
851 &self,
852 audio_data: &[f32],
853 conversion_type: &ConversionType,
854 quality: ProcessingQuality,
855 ) -> Result<Vec<f32>> {
856 if !self.enabled {
857 return Ok(audio_data.to_vec());
858 }
859
860 let mut result = audio_data.to_vec();
861
862 #[cfg(target_arch = "aarch64")]
863 {
864 match conversion_type {
865 ConversionType::PitchShift => {
866 self.neon_pitch_shift(&mut result, quality);
867 }
868 ConversionType::SpeedTransformation => {
869 self.neon_speed_transform(&mut result, quality);
870 }
871 ConversionType::VoiceMorphing => {
872 self.neon_voice_morph(&mut result, quality);
873 }
874 _ => {
875 }
877 }
878 }
879
880 #[cfg(not(target_arch = "aarch64"))]
881 {
882 let _ = (conversion_type, quality); for sample in result.iter_mut() {
885 *sample *= 0.95; }
887 }
888
889 Ok(result)
890 }
891
892 pub fn postprocess_audio(
894 &self,
895 mut audio_data: Vec<f32>,
896 _quality: ProcessingQuality,
897 ) -> Vec<f32> {
898 if !self.enabled {
899 return audio_data;
900 }
901
902 #[cfg(target_arch = "aarch64")]
903 {
904 self.neon_finalize_audio(&mut audio_data);
905 }
906
907 #[cfg(not(target_arch = "aarch64"))]
908 {
909 for sample in audio_data.iter_mut() {
911 *sample = sample.clamp(-1.0, 1.0);
912 }
913 }
914
915 audio_data
916 }
917
918 #[cfg(target_arch = "aarch64")]
921 fn neon_normalize_audio(&self, audio_data: &mut [f32]) {
922 for sample in audio_data.iter_mut() {
925 *sample = sample.clamp(-1.0, 1.0);
926 }
927 }
928
929 #[cfg(target_arch = "aarch64")]
930 fn neon_pitch_shift(&self, audio_data: &mut [f32], quality: ProcessingQuality) {
931 let shift_factor = match quality {
932 ProcessingQuality::Minimal => 1.05,
933 ProcessingQuality::Reduced => 1.03,
934 ProcessingQuality::Standard => 1.02,
935 ProcessingQuality::High => 1.01,
936 };
937
938 for sample in audio_data.iter_mut() {
940 *sample *= shift_factor;
941 }
942 }
943
944 #[cfg(target_arch = "aarch64")]
945 fn neon_speed_transform(&self, audio_data: &mut [f32], quality: ProcessingQuality) {
946 let speed_factor = match quality {
947 ProcessingQuality::Minimal => 0.95,
948 ProcessingQuality::Reduced => 0.97,
949 ProcessingQuality::Standard => 0.98,
950 ProcessingQuality::High => 0.99,
951 };
952
953 for sample in audio_data.iter_mut() {
955 *sample = (*sample * speed_factor).clamp(-1.0, 1.0);
956 }
957 }
958
959 #[cfg(target_arch = "aarch64")]
960 fn neon_voice_morph(&self, audio_data: &mut [f32], _quality: ProcessingQuality) {
961 for (i, sample) in audio_data.iter_mut().enumerate() {
963 let morph_factor = (i as f32 * 0.001).sin() * 0.1 + 1.0;
964 *sample *= morph_factor;
965 }
966 }
967
968 #[cfg(target_arch = "aarch64")]
969 fn neon_finalize_audio(&self, audio_data: &mut [f32]) {
970 for sample in audio_data.iter_mut() {
972 *sample = sample.clamp(-1.0, 1.0);
973 }
974 }
975}
976
977pub struct MobileConversionStats {
979 total_conversions: AtomicU64,
980 total_processing_time: AtomicU64, power_mode_changes: AtomicU32,
982 thermal_events: Arc<Mutex<HashMap<ThermalState, u32>>>,
983 conversion_types: Arc<Mutex<HashMap<ConversionType, u32>>>,
984 neon_accelerated: AtomicU64,
985 device_updates: AtomicU32,
986}
987
988impl MobileConversionStats {
989 fn new() -> Self {
990 Self {
991 total_conversions: AtomicU64::new(0),
992 total_processing_time: AtomicU64::new(0),
993 power_mode_changes: AtomicU32::new(0),
994 thermal_events: Arc::new(Mutex::new(HashMap::new())),
995 conversion_types: Arc::new(Mutex::new(HashMap::new())),
996 neon_accelerated: AtomicU64::new(0),
997 device_updates: AtomicU32::new(0),
998 }
999 }
1000
1001 fn record_conversion(&self, processing_time: Duration, conversion_type: ConversionType) {
1002 self.total_conversions.fetch_add(1, Ordering::Relaxed);
1003 self.total_processing_time
1004 .fetch_add(processing_time.as_nanos() as u64, Ordering::Relaxed);
1005
1006 if let Ok(mut types) = self.conversion_types.try_lock() {
1009 *types.entry(conversion_type).or_insert(0) += 1;
1010 }
1011 }
1012
1013 fn record_power_mode_change(&self, _mode: PowerMode) {
1014 self.power_mode_changes.fetch_add(1, Ordering::Relaxed);
1015 }
1016
1017 fn record_thermal_event(&self, thermal_state: ThermalState) {
1018 tokio::spawn({
1019 let thermal_events = Arc::clone(&self.thermal_events);
1020 async move {
1021 let mut events = thermal_events.lock().await;
1022 *events.entry(thermal_state).or_insert(0) += 1;
1023 }
1024 });
1025 }
1026
1027 fn record_device_update(&self, _thermal_state: ThermalState, _battery_percent: f64) {
1028 self.device_updates.fetch_add(1, Ordering::Relaxed);
1029 }
1030
1031 fn record_neon_acceleration(&self) {
1032 self.neon_accelerated.fetch_add(1, Ordering::Relaxed);
1033 }
1034
1035 fn get_statistics(&self) -> MobileConversionStatistics {
1036 let total_conversions = self.total_conversions.load(Ordering::Relaxed);
1037 let total_processing_time_ns = self.total_processing_time.load(Ordering::Relaxed);
1038
1039 let average_processing_time_ms = total_processing_time_ns
1040 .checked_div(total_conversions)
1041 .map(|t| t as f64 / 1_000_000.0)
1042 .unwrap_or(0.0);
1043
1044 MobileConversionStatistics {
1045 total_conversions,
1046 average_processing_time_ms,
1047 power_mode_changes: self.power_mode_changes.load(Ordering::Relaxed),
1048 neon_accelerated: self.neon_accelerated.load(Ordering::Relaxed),
1049 device_updates: self.device_updates.load(Ordering::Relaxed),
1050 thermal_events: HashMap::new(), conversion_types: HashMap::new(), }
1053 }
1054}
1055
1056#[derive(Debug, Clone, Serialize, Deserialize)]
1058pub struct MobileConversionStatistics {
1059 pub total_conversions: u64,
1061 pub average_processing_time_ms: f64,
1063 pub power_mode_changes: u32,
1065 pub neon_accelerated: u64,
1067 pub device_updates: u32,
1069 pub thermal_events: HashMap<ThermalState, u32>,
1071 pub conversion_types: HashMap<ConversionType, u32>,
1073}
1074
1075#[cfg(test)]
1076mod tests {
1077 use super::*;
1078
1079 #[test]
1080 fn test_mobile_platform_detection() {
1081 let platform = MobilePlatform::detect();
1082 assert!(matches!(
1083 platform,
1084 MobilePlatform::IOS
1085 | MobilePlatform::Android
1086 | MobilePlatform::GenericARM
1087 | MobilePlatform::Desktop
1088 ));
1089 }
1090
1091 #[test]
1092 fn test_mobile_device_info() {
1093 let info = MobileDeviceInfo::detect();
1094 assert!(!info.device_model.is_empty());
1095 assert!(info.cpu_cores > 0);
1096 assert!(info.ram_mb > 0);
1097 assert!(info.battery_percent >= 0.0 && info.battery_percent <= 100.0);
1098 }
1099
1100 #[test]
1101 fn test_power_mode_recommendation() {
1102 let mut info = MobileDeviceInfo::detect();
1103
1104 info.battery_percent = 5.0;
1106 assert_eq!(info.recommend_power_mode(), PowerMode::UltraPowerSaver);
1107
1108 info.battery_percent = 80.0;
1110 info.thermal_state = ThermalState::Critical;
1111 assert_eq!(info.recommend_power_mode(), PowerMode::UltraPowerSaver);
1112
1113 info.battery_percent = 70.0;
1115 info.thermal_state = ThermalState::Normal;
1116 assert_eq!(info.recommend_power_mode(), PowerMode::HighPerformance);
1117 }
1118
1119 #[test]
1120 fn test_mobile_config_creation() {
1121 let config = MobileConversionConfig::default();
1122 assert!(config.enable_power_management);
1123 assert!(config.enable_thermal_management);
1124 assert!(config.enable_memory_optimization);
1125 assert_eq!(config.target_memory_mb, 50.0);
1126 assert_eq!(config.max_concurrent_conversions, 2);
1127 }
1128
1129 #[tokio::test]
1130 async fn test_mobile_converter_creation() {
1131 let converter = MobileVoiceConverter::new().await;
1132 assert!(converter.is_ok());
1133 }
1134
1135 #[tokio::test]
1136 async fn test_power_mode_setting() {
1137 let converter = MobileVoiceConverter::new().await.unwrap();
1138
1139 assert!(converter
1140 .set_power_mode(PowerMode::PowerSaver)
1141 .await
1142 .is_ok());
1143 assert_eq!(converter.get_power_mode().await, PowerMode::PowerSaver);
1144
1145 assert!(converter
1146 .set_power_mode(PowerMode::HighPerformance)
1147 .await
1148 .is_ok());
1149 assert_eq!(converter.get_power_mode().await, PowerMode::HighPerformance);
1150 }
1151
1152 #[tokio::test]
1153 async fn test_mobile_conversion() {
1154 let converter = MobileVoiceConverter::new().await.unwrap();
1155
1156 let request = ConversionRequest::new(
1157 "test_mobile".to_string(),
1158 vec![0.1, -0.1, 0.2, -0.2],
1159 44100,
1160 ConversionType::PitchShift,
1161 ConversionTarget::new(VoiceCharacteristics::default()),
1162 );
1163
1164 let result = converter.convert_mobile_optimized(&request).await;
1165 assert!(result.is_ok());
1166
1167 let conversion_result = result.unwrap();
1168 assert_eq!(conversion_result.request_id, "test_mobile");
1169 assert!(conversion_result.success);
1170 }
1171
1172 #[tokio::test]
1173 async fn test_batch_conversion() {
1174 let converter = MobileVoiceConverter::new().await.unwrap();
1175
1176 let requests = vec![
1177 ConversionRequest::new(
1178 "batch_1".to_string(),
1179 vec![0.1, -0.1],
1180 44100,
1181 ConversionType::PitchShift,
1182 ConversionTarget::new(VoiceCharacteristics::default()),
1183 ),
1184 ConversionRequest::new(
1185 "batch_2".to_string(),
1186 vec![0.2, -0.2],
1187 44100,
1188 ConversionType::SpeedTransformation,
1189 ConversionTarget::new(VoiceCharacteristics::default()),
1190 ),
1191 ];
1192
1193 let results = converter.convert_batch_mobile(&requests).await;
1194 assert!(results.is_ok());
1195
1196 let conversion_results = results.unwrap();
1197 assert_eq!(conversion_results.len(), 2);
1198 assert!(conversion_results.iter().all(|r| r.success));
1199 }
1200
1201 #[test]
1202 fn test_neon_optimizer() {
1203 let optimizer = NeonOptimizer::new();
1204 let mut audio_data = vec![0.5, -0.5, 1.0, -1.0];
1205
1206 optimizer.preprocess_audio(&mut audio_data, ProcessingQuality::Standard);
1207
1208 for &sample in &audio_data {
1210 assert!(sample >= -1.0 && sample <= 1.0);
1211 }
1212
1213 let converted = optimizer.convert_audio(
1214 &audio_data,
1215 &ConversionType::PitchShift,
1216 ProcessingQuality::Standard,
1217 );
1218 assert!(converted.is_ok());
1219
1220 let result = optimizer.postprocess_audio(converted.unwrap(), ProcessingQuality::Standard);
1221 assert_eq!(result.len(), audio_data.len());
1222 }
1223
1224 #[test]
1225 fn test_thermal_state_conversion() {
1226 assert_eq!(
1227 MobileDeviceInfo::temperature_to_thermal_state(50.0),
1228 ThermalState::Normal
1229 );
1230 assert_eq!(
1231 MobileDeviceInfo::temperature_to_thermal_state(65.0),
1232 ThermalState::Warm
1233 );
1234 assert_eq!(
1235 MobileDeviceInfo::temperature_to_thermal_state(75.0),
1236 ThermalState::Hot
1237 );
1238 assert_eq!(
1239 MobileDeviceInfo::temperature_to_thermal_state(85.0),
1240 ThermalState::Critical
1241 );
1242 }
1243
1244 #[test]
1245 fn test_processing_quality() {
1246 assert_eq!(ProcessingQuality::Minimal.as_score(), 0.4);
1247 assert_eq!(ProcessingQuality::Reduced.as_score(), 0.6);
1248 assert_eq!(ProcessingQuality::Standard.as_score(), 0.8);
1249 assert_eq!(ProcessingQuality::High.as_score(), 1.0);
1250 }
1251
1252 #[test]
1253 fn test_mobile_stats() {
1254 let stats = MobileConversionStats::new();
1255
1256 stats.record_conversion(Duration::from_millis(100), ConversionType::PitchShift);
1257 stats.record_power_mode_change(PowerMode::PowerSaver);
1258 stats.record_device_update(ThermalState::Warm, 50.0);
1259
1260 let statistics = stats.get_statistics();
1261 assert_eq!(statistics.total_conversions, 1);
1262 assert!(statistics.average_processing_time_ms > 0.0);
1263 assert_eq!(statistics.power_mode_changes, 1);
1264 assert_eq!(statistics.device_updates, 1);
1265 }
1266
1267 #[tokio::test]
1268 async fn test_device_monitoring() {
1269 let converter = MobileVoiceConverter::new().await.unwrap();
1270
1271 let handle = converter.start_device_monitoring().await.unwrap();
1272
1273 tokio::time::sleep(Duration::from_millis(50)).await;
1275
1276 handle.abort();
1277
1278 }
1280}