""" Audio Segment Model Represents a contiguous portion of audio with speaker and timing information. """ from dataclasses import dataclass from enum import Enum from typing import Optional class SegmentType(Enum): """Classification of audio segment types.""" SPEECH = "speech" NONVERBAL = "nonverbal" SILENCE = "silence" OVERLAP = "overlap" # Multiple speakers talking simultaneously @dataclass class AudioSegment: """ Audio segment with time range and speaker information. IMPORTANT: This model stores ONLY metadata (timestamps, speaker info, classification). Audio data is NEVER stored in AudioSegment instances. Audio is extracted on-demand from source files using the stored timestamps during concatenation or processing. This metadata-only design enables memory-efficient processing of large audio files (>1 hour) by avoiding storage of thousands of audio arrays in memory. Attributes: start_time: Beginning timestamp in seconds end_time: Ending timestamp in seconds speaker_id: Identifier of the speaker in this segment confidence: Certainty of speaker identification (0.0-1.0) segment_type: Classification of the segment audio_file: Path to the source audio file (optional, for reference only) Usage Pattern: # Create segment with metadata only segment = AudioSegment( start_time=10.5, end_time=15.3, speaker_id="speaker_00", confidence=0.95, segment_type=SegmentType.SPEECH ) # Extract audio on-demand when needed start_sample = int(segment.start_time * sample_rate) end_sample = int(segment.end_time * sample_rate) segment_audio = source_audio[start_sample:end_sample] """ start_time: float end_time: float speaker_id: str confidence: float = 1.0 segment_type: SegmentType = SegmentType.SPEECH audio_file: Optional[str] = None def __post_init__(self): """Validate audio segment data.""" if self.start_time < 0: raise ValueError(f"Start time cannot be negative: {self.start_time}") if self.end_time <= self.start_time: raise ValueError( f"End time ({self.end_time}) must be after start time ({self.start_time})" ) if not 0.0 <= self.confidence <= 1.0: raise ValueError(f"Confidence must be between 0.0 and 1.0, got {self.confidence}") # Ensure no audio data is accidentally stored (metadata-only enforcement) if hasattr(self, "audio") or "audio" in self.__dict__: raise ValueError( "AudioSegment must not contain 'audio' attribute. " "Audio data should be extracted on-demand using timestamps." ) @property def duration(self) -> float: """Calculate duration of the segment in seconds.""" return self.end_time - self.start_time def overlaps_with(self, other: "AudioSegment") -> bool: """Check if this segment overlaps with another segment.""" return not (self.end_time <= other.start_time or other.end_time <= self.start_time) def contains_time(self, time: float) -> bool: """Check if a timestamp falls within this segment.""" return self.start_time <= time <= self.end_time def __repr__(self) -> str: return ( f"AudioSegment(" f"speaker='{self.speaker_id}', " f"time={self.start_time:.2f}-{self.end_time:.2f}s, " f"duration={self.duration:.2f}s, " f"confidence={self.confidence:.2f}, " f"type={self.segment_type.value})" )