File size: 3,637 Bytes
5009cb8
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
"""VoiceSettings value object for TTS voice configuration with validation."""

from dataclasses import dataclass
from typing import Optional
import re


@dataclass(frozen=True)
class VoiceSettings:
    """Value object representing voice settings for text-to-speech synthesis."""
    
    voice_id: str
    speed: float
    language: str
    pitch: Optional[float] = None
    volume: Optional[float] = None
    
    def __post_init__(self):
        """Validate voice settings after initialization."""
        self._validate()
    
    def _validate(self):
        """Validate voice settings properties."""
        if not isinstance(self.voice_id, str):
            raise TypeError("Voice ID must be a string")
        
        if not self.voice_id.strip():
            raise ValueError("Voice ID cannot be empty")
        
        # Voice ID should be alphanumeric with possible underscores/hyphens
        if not re.match(r'^[a-zA-Z0-9_-]+$', self.voice_id):
            raise ValueError(f"Invalid voice ID format: {self.voice_id}. Must contain only letters, numbers, underscores, and hyphens")
        
        if not isinstance(self.speed, (int, float)):
            raise TypeError("Speed must be a number")
        
        if not 0.1 <= self.speed <= 3.0:
            raise ValueError(f"Speed must be between 0.1 and 3.0, got {self.speed}")
        
        if not isinstance(self.language, str):
            raise TypeError("Language must be a string")
        
        if not self.language.strip():
            raise ValueError("Language cannot be empty")
        
        # Validate language code format (ISO 639-1 or ISO 639-3)
        if not re.match(r'^[a-z]{2,3}(-[A-Z]{2})?$', self.language):
            raise ValueError(f"Invalid language code format: {self.language}. Expected format: 'en', 'en-US', etc.")
        
        if self.pitch is not None:
            if not isinstance(self.pitch, (int, float)):
                raise TypeError("Pitch must be a number")
            
            if not -2.0 <= self.pitch <= 2.0:
                raise ValueError(f"Pitch must be between -2.0 and 2.0, got {self.pitch}")
        
        if self.volume is not None:
            if not isinstance(self.volume, (int, float)):
                raise TypeError("Volume must be a number")
            
            if not 0.0 <= self.volume <= 2.0:
                raise ValueError(f"Volume must be between 0.0 and 2.0, got {self.volume}")
    
    @property
    def is_default_speed(self) -> bool:
        """Check if speed is at default value (1.0)."""
        return abs(self.speed - 1.0) < 0.01
    
    @property
    def is_default_pitch(self) -> bool:
        """Check if pitch is at default value (0.0 or None)."""
        return self.pitch is None or abs(self.pitch) < 0.01
    
    @property
    def is_default_volume(self) -> bool:
        """Check if volume is at default value (1.0 or None)."""
        return self.volume is None or abs(self.volume - 1.0) < 0.01
    
    def with_speed(self, speed: float) -> 'VoiceSettings':
        """Create a new VoiceSettings with different speed."""
        return VoiceSettings(
            voice_id=self.voice_id,
            speed=speed,
            language=self.language,
            pitch=self.pitch,
            volume=self.volume
        )
    
    def with_pitch(self, pitch: Optional[float]) -> 'VoiceSettings':
        """Create a new VoiceSettings with different pitch."""
        return VoiceSettings(
            voice_id=self.voice_id,
            speed=self.speed,
            language=self.language,
            pitch=pitch,
            volume=self.volume
        )