fix: add support for partial words

This commit is contained in:
Pyae Sone Myo
2025-10-14 23:06:13 +06:30
parent 3b751322d3
commit b6b0997553

View File

@@ -827,6 +827,10 @@ class ElevenLabsHttpTTSService(WordTTSService):
# Store previous text for context within a turn # Store previous text for context within a turn
self._previous_text = "" self._previous_text = ""
# Track partial words that span across alignment chunks
self._partial_word = ""
self._partial_word_start_time = 0.0
def language_to_service_language(self, language: Language) -> Optional[str]: def language_to_service_language(self, language: Language) -> Optional[str]:
"""Convert pipecat Language to ElevenLabs language code. """Convert pipecat Language to ElevenLabs language code.
@@ -854,6 +858,8 @@ class ElevenLabsHttpTTSService(WordTTSService):
self._cumulative_time = 0 self._cumulative_time = 0
self._started = False self._started = False
self._previous_text = "" self._previous_text = ""
self._partial_word = ""
self._partial_word_start_time = 0.0
logger.debug(f"{self}: Reset internal state") logger.debug(f"{self}: Reset internal state")
async def start(self, frame: StartFrame): async def start(self, frame: StartFrame):
@@ -888,11 +894,13 @@ class ElevenLabsHttpTTSService(WordTTSService):
def calculate_word_times(self, alignment_info: Mapping[str, Any]) -> List[Tuple[str, float]]: def calculate_word_times(self, alignment_info: Mapping[str, Any]) -> List[Tuple[str, float]]:
"""Calculate word timing from character alignment data. """Calculate word timing from character alignment data.
This method handles partial words that may span across multiple alignment chunks.
Args: Args:
alignment_info: Character timing data from ElevenLabs. alignment_info: Character timing data from ElevenLabs.
Returns: Returns:
List of (word, timestamp) pairs. List of (word, timestamp) pairs for complete words in this chunk.
Example input data:: Example input data::
@@ -918,30 +926,28 @@ class ElevenLabsHttpTTSService(WordTTSService):
# Build the words and find their start times # Build the words and find their start times
words = [] words = []
word_start_times = [] word_start_times = []
current_word = "" # Start with any partial word from previous chunk
first_char_idx = -1 current_word = self._partial_word
word_start_time = self._partial_word_start_time if self._partial_word else None
for i, char in enumerate(chars): for i, char in enumerate(chars):
if char == " ": if char == " ":
if current_word: # Only add non-empty words if current_word: # Only add non-empty words
words.append(current_word) words.append(current_word)
# Use time of the first character of the word, offset by cumulative time word_start_times.append(word_start_time)
word_start_times.append(
self._cumulative_time + char_start_times[first_char_idx]
)
current_word = "" current_word = ""
first_char_idx = -1 word_start_time = None
else: else:
if not current_word: # This is the first character of a new word if word_start_time is None: # First character of a new word
first_char_idx = i # Use time of the first character of the word, offset by cumulative time
word_start_time = self._cumulative_time + char_start_times[i]
current_word += char current_word += char
# Don't forget the last word if there's no trailing space # Store any incomplete word at the end of this chunk
if current_word and first_char_idx >= 0: self._partial_word = current_word if current_word else ""
words.append(current_word) self._partial_word_start_time = word_start_time if word_start_time is not None else 0.0
word_start_times.append(self._cumulative_time + char_start_times[first_char_idx])
# Create word-time pairs # Create word-time pairs for complete words only
word_times = list(zip(words, word_start_times)) word_times = list(zip(words, word_start_times))
return word_times return word_times