Spaces:
Build error
Build error
File size: 4,415 Bytes
5009cb8 0f99c8d 5009cb8 0f99c8d 5009cb8 0f99c8d 5009cb8 0f99c8d 5009cb8 0f99c8d 5009cb8 0f99c8d 5009cb8 0f99c8d 5009cb8 0f99c8d 5009cb8 0f99c8d 5009cb8 0f99c8d 5009cb8 0f99c8d 5009cb8 0f99c8d 5009cb8 0f99c8d 5009cb8 0f99c8d 5009cb8 0f99c8d 5009cb8 0f99c8d 5009cb8 0f99c8d 5009cb8 0f99c8d 5009cb8 0f99c8d 5009cb8 0f99c8d 5009cb8 0f99c8d 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 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 |
"""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
audio_prompt_path: Optional[str] = None # For voice cloning (e.g., Chatterbox)
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}")
if self.audio_prompt_path is not None:
if not isinstance(self.audio_prompt_path, str):
raise TypeError("Audio prompt path must be a string")
if not self.audio_prompt_path.strip():
raise ValueError("Audio prompt path cannot be empty")
@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,
audio_prompt_path=self.audio_prompt_path
)
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,
audio_prompt_path=self.audio_prompt_path
)
def with_audio_prompt(self, audio_prompt_path: Optional[str]) -> 'VoiceSettings':
"""Create a new VoiceSettings with different audio prompt path."""
return VoiceSettings(
voice_id=self.voice_id,
speed=self.speed,
language=self.language,
pitch=self.pitch,
volume=self.volume,
audio_prompt_path=audio_prompt_path
) |