refactor: Switch TTSBackend from ABC to Protocol

This commit is contained in:
Artem Akymenko
2026-07-05 13:47:31 +00:00
parent 50b4d6872a
commit 9833bb0843
2 changed files with 50 additions and 101 deletions
+46 -98
View File
@@ -1,117 +1,65 @@
"""
Minimal TTS Backend Interface
TTS Backend Interface
This module defines a minimal interface for TTS backends to enable future
extensibility while maintaining backward compatibility with existing Kokoro
implementation.
This module defines the protocol for TTS backends.
"""
from abc import ABC, abstractmethod
from typing import Any, Iterator, Optional, Union
from typing import Protocol, List, Dict, Any
class TTSBackend(ABC):
class TTSBackend(Protocol):
"""
Minimal interface for TTS backends.
This interface is designed to be minimal and focused on the essential
operations needed for text-to-speech conversion.
Protocol for TTS backends.
All TTS backends must implement this interface to be compatible
with the application.
"""
@abstractmethod
def __call__(
self,
text: str,
voice: Union[str, Any],
speed: float = 1.0,
**kwargs: Any
) -> Iterator[Any]:
def __init__(self, **kwargs) -> None:
"""
Generate speech segments from text.
Initialize the TTS backend.
Args:
text: Text to convert to speech
voice: Voice specification or object
speed: Speed multiplier for speech
**kwargs: Additional backend-specific parameters
Yields:
Speech segments (audio data, timing info, etc.)
**kwargs: Backend-specific configuration parameters
"""
pass
...
class KokoroTTSBackend(TTSBackend):
"""
Implementation of TTSBackend using Kokoro.
This class provides the concrete implementation that maintains
the existing behavior while conforming to the TTSBackend interface.
"""
def __init__(self, lang_code: str, repo_id: str = "hexgrad/Kokoro-82M", device: str = "cpu"):
def synthesize(self, text: str, **kwargs) -> bytes:
"""
Initialize Kokoro backend.
Synthesize speech from text.
Args:
lang_code: Language code for the model
repo_id: Repository ID for the Kokoro model
device: Device to run the model on (cpu, cuda, etc.)
"""
self.lang_code = lang_code
self.repo_id = repo_id
self.device = device
self._pipeline = None
text: Text to synthesize
**kwargs: Additional parameters for synthesis
def _get_pipeline(self):
"""Lazy initialization of the Kokoro pipeline."""
if self._pipeline is None:
from abogen.utils import load_numpy_kpipeline
_, KPipeline = load_numpy_kpipeline()
try:
self._pipeline = KPipeline(
lang_code=self.lang_code,
repo_id=self.repo_id,
device=self.device
)
except RuntimeError as e:
if "CUDA" in str(e) and self.device != "cpu":
# Fall back to CPU if CUDA fails
self._pipeline = KPipeline(
lang_code=self.lang_code,
repo_id=self.repo_id,
device="cpu"
)
else:
raise
return self._pipeline
Returns:
Audio data as bytes
"""
...
def __call__(
self,
text: str,
voice: Union[str, Any],
speed: float = 1.0,
split_pattern: str = r"\n+",
**kwargs: Any
) -> Iterator[Any]:
def get_available_voices(self) -> List[str]:
"""
Generate speech segments from text using Kokoro.
Args:
text: Text to convert to speech
voice: Voice specification or object
speed: Speed multiplier for speech
split_pattern: Pattern to split text into segments
**kwargs: Additional parameters passed to the pipeline
Yields:
Speech segments
Get list of available voices.
Returns:
List of voice identifiers
"""
pipeline = self._get_pipeline()
return pipeline(
text,
voice=voice,
speed=speed,
split_pattern=split_pattern,
**kwargs
)
...
def get_supported_formats(self) -> List[str]:
"""
Get list of supported audio formats.
Returns:
List of supported audio formats
"""
...
def get_info(self) -> Dict[str, Any]:
"""
Get backend information.
Returns:
Dictionary with backend information
"""
...
+4 -3
View File
@@ -41,7 +41,7 @@ from abogen.utils import (
load_config,
load_numpy_kpipeline,
)
from abogen.tts_backend import KokoroTTSBackend
from abogen.tts_backend import TTSBackend
from abogen.voice_cache import ensure_voice_assets
from abogen.voice_formulas import extract_voice_ids, get_new_voice
from abogen.voice_profiles import load_profiles, normalize_profile_entry
@@ -1595,8 +1595,9 @@ def run_conversion_job(job: Job) -> None:
device = "cpu"
if not disable_gpu:
device = _select_device()
# Create KokoroTTSBackend instance instead of directly instantiating KPipeline
pipelines[provider_norm] = KokoroTTSBackend(
_np, KPipeline = load_numpy_kpipeline()
# Create KPipeline instance directly (conforms to TTSBackend protocol)
pipelines[provider_norm] = KPipeline(
lang_code=job.language,
repo_id="hexgrad/Kokoro-82M",
device=device