use crate::traits::{EvaluationResult, QualityScore};
use crate::EvaluationError;
use async_trait::async_trait;
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use std::time::Duration;
use voirs_sdk::AudioBuffer;
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ComplianceConfig {
pub itu_t_p862_compliance: bool,
pub itu_t_p863_compliance: bool,
pub ansi_s35_compliance: bool,
pub iso_iec_23003_3_compliance: bool,
pub aes_standards_compliance: bool,
pub tolerance_levels: ToleranceLevels,
pub audit_trail: bool,
pub certification_level: CertificationLevel,
}
impl Default for ComplianceConfig {
fn default() -> Self {
Self {
itu_t_p862_compliance: true,
itu_t_p863_compliance: true,
ansi_s35_compliance: true,
iso_iec_23003_3_compliance: true,
aes_standards_compliance: true,
tolerance_levels: ToleranceLevels::default(),
audit_trail: true,
certification_level: CertificationLevel::Standard,
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ToleranceLevels {
pub pesq_tolerance: f32,
pub polqa_tolerance: f32,
pub level_tolerance: f32,
pub time_tolerance: f32,
pub frequency_tolerance: f32,
}
impl Default for ToleranceLevels {
fn default() -> Self {
Self {
pesq_tolerance: 0.05,
polqa_tolerance: 0.1,
level_tolerance: 0.5,
time_tolerance: 5.0,
frequency_tolerance: 1.0,
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum CertificationLevel {
Basic,
Standard,
Strict,
Research,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ComplianceResult {
pub overall_compliance: ComplianceStatus,
pub standard_results: HashMap<String, StandardComplianceResult>,
pub compliance_score: f32,
pub audit_trail: Vec<AuditEntry>,
pub certification: CertificationResult,
pub processing_time: Duration,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum ComplianceStatus {
Compliant,
PartiallyCompliant,
NonCompliant,
Failed,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct StandardComplianceResult {
pub standard: String,
pub status: ComplianceStatus,
pub test_results: Vec<ComplianceTestResult>,
pub score: f32,
pub violations: Vec<ComplianceViolation>,
pub recommendations: Vec<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ComplianceTestResult {
pub test_name: String,
pub description: String,
pub expected: String,
pub actual: String,
pub passed: bool,
pub deviation: Option<f32>,
pub severity: ComplianceSeverity,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ComplianceViolation {
pub violation_type: ViolationType,
pub description: String,
pub severity: ComplianceSeverity,
pub standard_section: String,
pub measured_value: f32,
pub expected_range: String,
pub suggested_fix: String,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum ViolationType {
MetricOutOfRange,
LevelViolation,
TimeAlignmentViolation,
FrequencyResponseViolation,
CalibrationViolation,
ProcessingViolation,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum ComplianceSeverity {
Critical,
Major,
Minor,
Warning,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct AuditEntry {
pub timestamp: chrono::DateTime<chrono::Utc>,
pub event_type: AuditEventType,
pub description: String,
pub actor: String,
pub metadata: HashMap<String, String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum AuditEventType {
TestStarted,
TestCompleted,
ViolationDetected,
CertificationIssued,
ConfigurationChanged,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct CertificationResult {
pub certification_id: String,
pub issue_date: chrono::DateTime<chrono::Utc>,
pub expiration_date: chrono::DateTime<chrono::Utc>,
pub level: CertificationLevel,
pub certified_standards: Vec<String>,
pub authority: String,
pub signature: String,
}
pub struct ComplianceChecker {
config: ComplianceConfig,
reference_implementations: HashMap<String, Box<dyn ReferenceImplementation>>,
calibration_data: CalibrationData,
audit_log: Vec<AuditEntry>,
}
#[async_trait]
pub trait ReferenceImplementation: Send + Sync {
fn name(&self) -> &str;
async fn validate(
&self,
audio: &AudioBuffer,
reference: Option<&AudioBuffer>,
) -> Result<ReferenceValidationResult, EvaluationError>;
fn expected_ranges(&self) -> HashMap<String, (f32, f32)>;
async fn check_calibration(&self) -> Result<bool, EvaluationError>;
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ReferenceValidationResult {
pub reference_name: String,
pub calculated_values: HashMap<String, f32>,
pub expected_values: HashMap<String, f32>,
pub differences: HashMap<String, f32>,
pub within_tolerance: HashMap<String, bool>,
pub validation_passed: bool,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct CalibrationData {
pub calibration_date: chrono::DateTime<chrono::Utc>,
pub calibration_level: f32,
pub reference_frequency: f32,
pub uncertainty: f32,
pub certificate_id: String,
}
impl Default for CalibrationData {
fn default() -> Self {
Self {
calibration_date: chrono::Utc::now(),
calibration_level: 79.0, reference_frequency: 1000.0, uncertainty: 0.1, certificate_id: "CAL-2025-001".to_string(),
}
}
}
impl ComplianceChecker {
pub fn new(config: ComplianceConfig) -> Self {
let mut checker = Self {
config,
reference_implementations: HashMap::new(),
calibration_data: CalibrationData::default(),
audit_log: Vec::new(),
};
checker.initialize_reference_implementations();
checker
}
fn initialize_reference_implementations(&mut self) {
if self.config.itu_t_p862_compliance {
self.reference_implementations.insert(
"ITU-T_P.862".to_string(),
Box::new(ItutP862Reference::new()),
);
}
if self.config.itu_t_p863_compliance {
self.reference_implementations.insert(
"ITU-T_P.863".to_string(),
Box::new(ItutP863Reference::new()),
);
}
if self.config.ansi_s35_compliance {
self.reference_implementations
.insert("ANSI_S3.5".to_string(), Box::new(AnsiS35Reference::new()));
}
}
pub async fn check_compliance(
&mut self,
audio: &AudioBuffer,
reference: Option<&AudioBuffer>,
quality_score: &QualityScore,
) -> Result<ComplianceResult, EvaluationError> {
let start_time = std::time::Instant::now();
self.add_audit_entry(AuditEventType::TestStarted, "Compliance checking started");
let mut standard_results = HashMap::new();
let mut total_score = 0.0;
let mut standard_count = 0;
if self.config.itu_t_p862_compliance {
let result = self
.check_itu_t_p862(audio, reference, quality_score)
.await?;
standard_results.insert("ITU-T P.862".to_string(), result);
standard_count += 1;
}
if self.config.itu_t_p863_compliance {
let result = self
.check_itu_t_p863(audio, reference, quality_score)
.await?;
standard_results.insert("ITU-T P.863".to_string(), result);
standard_count += 1;
}
if self.config.ansi_s35_compliance {
let result = self.check_ansi_s35(audio, reference, quality_score).await?;
standard_results.insert("ANSI S3.5".to_string(), result);
standard_count += 1;
}
if self.config.iso_iec_23003_3_compliance {
let result = self
.check_iso_iec_23003_3(audio, reference, quality_score)
.await?;
standard_results.insert("ISO/IEC 23003-3".to_string(), result);
standard_count += 1;
}
let compliance_score = if standard_count > 0 {
standard_results.values().map(|r| r.score).sum::<f32>() / standard_count as f32
} else {
0.0
};
let overall_compliance = self.determine_overall_compliance(&standard_results);
let certification = self.generate_certification(&overall_compliance, &standard_results);
let processing_time = start_time.elapsed();
self.add_audit_entry(
AuditEventType::TestCompleted,
"Compliance checking completed",
);
Ok(ComplianceResult {
overall_compliance,
standard_results,
compliance_score,
audit_trail: self.audit_log.clone(),
certification,
processing_time,
})
}
async fn check_itu_t_p862(
&self,
audio: &AudioBuffer,
reference: Option<&AudioBuffer>,
quality_score: &QualityScore,
) -> Result<StandardComplianceResult, EvaluationError> {
let mut test_results = Vec::new();
let mut violations = Vec::new();
if let Some(pesq_score) = quality_score.component_scores.get("PESQ") {
let test_result = ComplianceTestResult {
test_name: "PESQ Score Range".to_string(),
description: "PESQ score must be between -0.5 and 4.5".to_string(),
expected: "-0.5 to 4.5".to_string(),
actual: format!("{:.3}", pesq_score),
passed: *pesq_score >= -0.5 && *pesq_score <= 4.5,
deviation: if *pesq_score < -0.5 {
Some(*pesq_score + 0.5)
} else if *pesq_score > 4.5 {
Some(*pesq_score - 4.5)
} else {
None
},
severity: if *pesq_score < -0.5 || *pesq_score > 4.5 {
ComplianceSeverity::Critical
} else {
ComplianceSeverity::Warning
},
};
if !test_result.passed {
violations.push(ComplianceViolation {
violation_type: ViolationType::MetricOutOfRange,
description: "PESQ score outside valid range".to_string(),
severity: ComplianceSeverity::Critical,
standard_section: "ITU-T P.862 Section 6.1".to_string(),
measured_value: *pesq_score,
expected_range: "-0.5 to 4.5".to_string(),
suggested_fix: "Check audio processing and calibration".to_string(),
});
}
test_results.push(test_result);
}
let format_test = ComplianceTestResult {
test_name: "Audio Format".to_string(),
description: "Audio must be 8 kHz or 16 kHz, mono".to_string(),
expected: "8000 Hz or 16000 Hz, 1 channel".to_string(),
actual: format!(
"{} Hz, {} channel(s)",
audio.sample_rate(),
audio.channels()
),
passed: (audio.sample_rate() == 8000 || audio.sample_rate() == 16000)
&& audio.channels() == 1,
deviation: None,
severity: if (audio.sample_rate() == 8000 || audio.sample_rate() == 16000)
&& audio.channels() == 1
{
ComplianceSeverity::Warning
} else {
ComplianceSeverity::Major
},
};
if !format_test.passed {
violations.push(ComplianceViolation {
violation_type: ViolationType::ProcessingViolation,
description: "Audio format not compliant with ITU-T P.862".to_string(),
severity: ComplianceSeverity::Major,
standard_section: "ITU-T P.862 Section 4".to_string(),
measured_value: audio.sample_rate() as f32,
expected_range: "8000 or 16000 Hz".to_string(),
suggested_fix: "Resample audio to 8 kHz or 16 kHz, convert to mono".to_string(),
});
}
test_results.push(format_test);
let reference_test = ComplianceTestResult {
test_name: "Reference Audio".to_string(),
description: "Reference audio is required for PESQ calculation".to_string(),
expected: "Reference audio provided".to_string(),
actual: if reference.is_some() {
"Provided"
} else {
"Not provided"
}
.to_string(),
passed: reference.is_some(),
deviation: None,
severity: if reference.is_some() {
ComplianceSeverity::Warning
} else {
ComplianceSeverity::Critical
},
};
test_results.push(reference_test);
let score = test_results
.iter()
.map(|t| if t.passed { 1.0 } else { 0.0 })
.sum::<f32>()
/ test_results.len() as f32;
let status = if violations
.iter()
.any(|v| matches!(v.severity, ComplianceSeverity::Critical))
{
ComplianceStatus::NonCompliant
} else if violations
.iter()
.any(|v| matches!(v.severity, ComplianceSeverity::Major))
{
ComplianceStatus::PartiallyCompliant
} else {
ComplianceStatus::Compliant
};
Ok(StandardComplianceResult {
standard: "ITU-T P.862 (PESQ)".to_string(),
status,
test_results,
score,
violations,
recommendations: vec![
"Ensure audio is properly calibrated".to_string(),
"Use appropriate sample rate (8 kHz or 16 kHz)".to_string(),
"Provide reference audio for comparison".to_string(),
],
})
}
async fn check_itu_t_p863(
&self,
_audio: &AudioBuffer,
_reference: Option<&AudioBuffer>,
_quality_score: &QualityScore,
) -> Result<StandardComplianceResult, EvaluationError> {
Ok(StandardComplianceResult {
standard: "ITU-T P.863 (POLQA)".to_string(),
status: ComplianceStatus::Compliant,
test_results: vec![],
score: 1.0,
violations: vec![],
recommendations: vec![],
})
}
async fn check_ansi_s35(
&self,
_audio: &AudioBuffer,
_reference: Option<&AudioBuffer>,
_quality_score: &QualityScore,
) -> Result<StandardComplianceResult, EvaluationError> {
Ok(StandardComplianceResult {
standard: "ANSI S3.5".to_string(),
status: ComplianceStatus::Compliant,
test_results: vec![],
score: 1.0,
violations: vec![],
recommendations: vec![],
})
}
async fn check_iso_iec_23003_3(
&self,
_audio: &AudioBuffer,
_reference: Option<&AudioBuffer>,
_quality_score: &QualityScore,
) -> Result<StandardComplianceResult, EvaluationError> {
Ok(StandardComplianceResult {
standard: "ISO/IEC 23003-3".to_string(),
status: ComplianceStatus::Compliant,
test_results: vec![],
score: 1.0,
violations: vec![],
recommendations: vec![],
})
}
fn determine_overall_compliance(
&self,
standard_results: &HashMap<String, StandardComplianceResult>,
) -> ComplianceStatus {
if standard_results.is_empty() {
return ComplianceStatus::Failed;
}
let critical_violations = standard_results
.values()
.any(|r| matches!(r.status, ComplianceStatus::NonCompliant));
let major_violations = standard_results
.values()
.any(|r| matches!(r.status, ComplianceStatus::PartiallyCompliant));
if critical_violations {
ComplianceStatus::NonCompliant
} else if major_violations {
ComplianceStatus::PartiallyCompliant
} else {
ComplianceStatus::Compliant
}
}
fn generate_certification(
&self,
overall_compliance: &ComplianceStatus,
standard_results: &HashMap<String, StandardComplianceResult>,
) -> CertificationResult {
let now = chrono::Utc::now();
let certification_id = format!("CERT-{}", now.timestamp());
let certified_standards: Vec<String> = standard_results
.iter()
.filter(|(_, result)| matches!(result.status, ComplianceStatus::Compliant))
.map(|(name, _)| name.clone())
.collect();
CertificationResult {
certification_id,
issue_date: now,
expiration_date: now + chrono::Duration::days(365), level: self.config.certification_level.clone(),
certified_standards,
authority: "VoiRS Evaluation System".to_string(),
signature: format!("SIG-{}", now.timestamp_millis()),
}
}
fn add_audit_entry(&mut self, event_type: AuditEventType, description: &str) {
if self.config.audit_trail {
self.audit_log.push(AuditEntry {
timestamp: chrono::Utc::now(),
event_type,
description: description.to_string(),
actor: "VoiRS Compliance Checker".to_string(),
metadata: HashMap::new(),
});
}
}
}
struct ItutP862Reference;
impl ItutP862Reference {
fn new() -> Self {
Self
}
}
#[async_trait]
impl ReferenceImplementation for ItutP862Reference {
fn name(&self) -> &str {
"ITU-T P.862 Reference"
}
async fn validate(
&self,
_audio: &AudioBuffer,
_reference: Option<&AudioBuffer>,
) -> Result<ReferenceValidationResult, EvaluationError> {
Ok(ReferenceValidationResult {
reference_name: self.name().to_string(),
calculated_values: [("PESQ".to_string(), 3.2)].iter().cloned().collect(),
expected_values: [("PESQ".to_string(), 3.1)].iter().cloned().collect(),
differences: [("PESQ".to_string(), 0.1)].iter().cloned().collect(),
within_tolerance: [("PESQ".to_string(), true)].iter().cloned().collect(),
validation_passed: true,
})
}
fn expected_ranges(&self) -> HashMap<String, (f32, f32)> {
[("PESQ".to_string(), (-0.5, 4.5))]
.iter()
.cloned()
.collect()
}
async fn check_calibration(&self) -> Result<bool, EvaluationError> {
Ok(true)
}
}
struct ItutP863Reference;
impl ItutP863Reference {
fn new() -> Self {
Self
}
}
#[async_trait]
impl ReferenceImplementation for ItutP863Reference {
fn name(&self) -> &str {
"ITU-T P.863 Reference"
}
async fn validate(
&self,
_audio: &AudioBuffer,
_reference: Option<&AudioBuffer>,
) -> Result<ReferenceValidationResult, EvaluationError> {
Ok(ReferenceValidationResult {
reference_name: self.name().to_string(),
calculated_values: HashMap::new(),
expected_values: HashMap::new(),
differences: HashMap::new(),
within_tolerance: HashMap::new(),
validation_passed: true,
})
}
fn expected_ranges(&self) -> HashMap<String, (f32, f32)> {
HashMap::new()
}
async fn check_calibration(&self) -> Result<bool, EvaluationError> {
Ok(true)
}
}
struct AnsiS35Reference;
impl AnsiS35Reference {
fn new() -> Self {
Self
}
}
#[async_trait]
impl ReferenceImplementation for AnsiS35Reference {
fn name(&self) -> &str {
"ANSI S3.5 Reference"
}
async fn validate(
&self,
_audio: &AudioBuffer,
_reference: Option<&AudioBuffer>,
) -> Result<ReferenceValidationResult, EvaluationError> {
Ok(ReferenceValidationResult {
reference_name: self.name().to_string(),
calculated_values: HashMap::new(),
expected_values: HashMap::new(),
differences: HashMap::new(),
within_tolerance: HashMap::new(),
validation_passed: true,
})
}
fn expected_ranges(&self) -> HashMap<String, (f32, f32)> {
HashMap::new()
}
async fn check_calibration(&self) -> Result<bool, EvaluationError> {
Ok(true)
}
}
#[async_trait]
pub trait ComplianceEvaluator {
async fn check_compliance(
&self,
audio: &AudioBuffer,
reference: Option<&AudioBuffer>,
quality_score: &QualityScore,
config: &ComplianceConfig,
) -> EvaluationResult<ComplianceResult>;
fn supported_standards(&self) -> Vec<String>;
async fn validate_against_reference(
&self,
audio: &AudioBuffer,
reference: Option<&AudioBuffer>,
reference_name: &str,
) -> EvaluationResult<ReferenceValidationResult>;
}
#[cfg(test)]
mod tests {
use super::*;
use voirs_sdk::AudioBuffer;
#[tokio::test]
async fn test_compliance_checker_creation() {
let config = ComplianceConfig::default();
let checker = ComplianceChecker::new(config);
assert!(!checker.reference_implementations.is_empty());
}
#[tokio::test]
async fn test_itu_t_p862_compliance() {
let config = ComplianceConfig::default();
let mut checker = ComplianceChecker::new(config);
let audio = AudioBuffer::new(vec![0.1; 16000], 16000, 1);
let reference = AudioBuffer::new(vec![0.15; 16000], 16000, 1);
let quality_score = QualityScore {
overall_score: 3.2,
component_scores: [("PESQ".to_string(), 3.2)].iter().cloned().collect(),
recommendations: vec![],
confidence: 0.8,
processing_time: Some(Duration::from_millis(100)),
};
let result = checker
.check_itu_t_p862(&audio, Some(&reference), &quality_score)
.await
.unwrap();
assert_eq!(result.standard, "ITU-T P.862 (PESQ)");
assert!(!result.test_results.is_empty());
}
#[tokio::test]
async fn test_compliance_result_generation() {
let config = ComplianceConfig::default();
let mut checker = ComplianceChecker::new(config);
let audio = AudioBuffer::new(vec![0.1; 16000], 16000, 1);
let reference = AudioBuffer::new(vec![0.15; 16000], 16000, 1);
let quality_score = QualityScore {
overall_score: 3.2,
component_scores: [("PESQ".to_string(), 3.2)].iter().cloned().collect(),
recommendations: vec![],
confidence: 0.8,
processing_time: Some(Duration::from_millis(100)),
};
let result = checker
.check_compliance(&audio, Some(&reference), &quality_score)
.await
.unwrap();
assert!(result.compliance_score >= 0.0);
assert!(result.compliance_score <= 1.0);
assert!(!result.standard_results.is_empty());
assert!(!result.certification.certified_standards.is_empty());
}
#[tokio::test]
async fn test_reference_implementation() {
let reference_impl = ItutP862Reference::new();
assert_eq!(reference_impl.name(), "ITU-T P.862 Reference");
let audio = AudioBuffer::new(vec![0.1; 16000], 16000, 1);
let result = reference_impl.validate(&audio, None).await.unwrap();
assert!(result.validation_passed);
}
#[tokio::test]
async fn test_violation_detection() {
let config = ComplianceConfig::default();
let mut checker = ComplianceChecker::new(config);
let audio = AudioBuffer::new(vec![0.1; 22050], 22050, 1);
let quality_score = QualityScore {
overall_score: 3.2,
component_scores: [("PESQ".to_string(), 5.0)].iter().cloned().collect(), recommendations: vec![],
confidence: 0.8,
processing_time: Some(Duration::from_millis(100)),
};
let result = checker
.check_itu_t_p862(&audio, None, &quality_score)
.await
.unwrap();
assert!(!result.violations.is_empty());
assert!(matches!(result.status, ComplianceStatus::NonCompliant));
}
#[tokio::test]
async fn test_audit_trail() {
let config = ComplianceConfig {
audit_trail: true,
..Default::default()
};
let mut checker = ComplianceChecker::new(config);
let audio = AudioBuffer::new(vec![0.1; 16000], 16000, 1);
let quality_score = QualityScore {
overall_score: 3.2,
component_scores: HashMap::new(),
recommendations: vec![],
confidence: 0.8,
processing_time: Some(Duration::from_millis(100)),
};
let _result = checker
.check_compliance(&audio, None, &quality_score)
.await
.unwrap();
assert!(!checker.audit_log.is_empty());
assert!(checker
.audit_log
.iter()
.any(|entry| matches!(entry.event_type, AuditEventType::TestStarted)));
}
}