aboutsummaryrefslogtreecommitdiff
path: root/app/converter/tts.py
diff options
context:
space:
mode:
Diffstat (limited to 'app/converter/tts.py')
-rw-r--r--app/converter/tts.py61
1 files changed, 13 insertions, 48 deletions
diff --git a/app/converter/tts.py b/app/converter/tts.py
index 6ccef56..842cc0c 100644
--- a/app/converter/tts.py
+++ b/app/converter/tts.py
@@ -278,11 +278,6 @@ def whisper_backend_available() -> Optional[str]:
return None
-# 150 wpm is a typical spoken pace; used only to size the HTTP request
-# timeout for long audio.cpp generations (not as a correctness check).
-_ESTIMATED_WORDS_PER_MINUTE = 150
-
-
class _BaseTTSClient:
"""Shared chunk retry logic, heartbeat, and chunk file bookkeeping."""
@@ -327,15 +322,10 @@ class _BaseTTSClient:
return None
@contextlib.contextmanager
- def _chunk_heartbeat(self, chunk_num: int, label: Optional[str] = None):
- """Print a periodic "still working" message while a request generates.
-
- ``label`` overrides the default "Chunk {chunk_num}" subject, for
- backends that send one request per chapter without client-side
- chunking (the audio.cpp default) where "chunk" would be misleading.
- """
+ def _chunk_heartbeat(self, chunk_num: int):
+ """Print a periodic "still working" message while a request generates."""
stop = threading.Event()
- subject = label if label is not None else f"Chunk {chunk_num}"
+ subject = f"Chunk {chunk_num}"
def _beat():
start = time.time()
@@ -796,20 +786,14 @@ class AudioCppTTSClient(_BaseTTSClient):
the request's "options" object, which is the server's generic
pass-through for per-model controls.
- Chunking: the server does its own long-form text chunking for every
- family (its ``text_chunk_size`` option, with a per-family default), so
- by default each chapter is sent as a single request and the audio
- comes back already stitched. With ``chunk_text=True`` (the --chunk CLI
- flag), text is instead split client-side into CHUNK_SIZE-word
- sub-requests, which may needlessly double-chunk — the warning is
- printed by the CLI.
-
- Each response is a complete WAV file, so sub-request audio is
- concatenated with the same lossless path used for the Qwen client.
+ Chunking: text is split client-side into sub-requests of at most
+ config.CHUNK_SIZE words each; each sub-request returns a complete
+ WAV file and the parts are concatenated with the same lossless path
+ used for the Qwen client.
"""
def __init__(self, voice: Optional[str] = None, language: Optional[str] = None,
- api_url: Optional[str] = None, chunk_text: bool = False,
+ api_url: Optional[str] = None,
model_id: Optional[str] = None,
instructions: Optional[str] = None,
request_options: Optional[Dict[str, str]] = None):
@@ -844,10 +828,6 @@ class AudioCppTTSClient(_BaseTTSClient):
# speakers gets its voice from the instruction alone (no voice field).
self.design_mode = False
self.instruction_voice = False
- # When False (default), each chapter is sent as one request and the
- # server does its own long-form chunking (text_chunk_size); when True,
- # text is split client-side into CHUNK_SIZE-word sub-requests first.
- self.chunk_text = bool(chunk_text)
# Family and task of the selected model entry and the family's request
# profile; all are resolved from GET /v1/models during _connect.
self.family = ""
@@ -1217,11 +1197,6 @@ class AudioCppTTSClient(_BaseTTSClient):
url, data=json.dumps(payload).encode("utf-8"),
headers={"Content-Type": "application/json"}, method="POST")
timeout = config.API_TIMEOUT
- if not self.chunk_text:
- # Estimated audio duration at 150 wpm, doubled plus a minute of
- # slack, bounded below by the configured per-request timeout.
- estimated_seconds = 60.0 * len(text.split()) / _ESTIMATED_WORDS_PER_MINUTE
- timeout = max(timeout, int(estimated_seconds * 2) + 60)
try:
with urllib.request.urlopen(request, timeout=timeout) as response:
wav = response.read()
@@ -1259,28 +1234,18 @@ class AudioCppTTSClient(_BaseTTSClient):
def generate_chunk(self, text: str, chunk_num: int) -> Optional[str]:
"""Generate one audio chunk; returns its path in the chunks folder.
- By default the whole text goes out as a single request and the
- server does its own long-form chunking (see the class docstring).
- With ``chunk_text=True`` (--chunk), the text is split into
- sub-requests of at most ``config.CHUNK_SIZE`` words each; each
- sub-request returns a complete WAV file and the parts are
- concatenated into one chunk file.
+ The text is split into sub-requests of at most ``config.CHUNK_SIZE``
+ words each; each sub-request returns a complete WAV file and the
+ parts are concatenated into one chunk file.
"""
try:
- if self.chunk_text:
- sub_texts = split_into_chunks(text, max_words=config.CHUNK_SIZE)
- elif text.strip():
- sub_texts = [text]
- else:
- sub_texts = []
+ sub_texts = split_into_chunks(text, max_words=config.CHUNK_SIZE)
if not sub_texts:
raise RuntimeError("No text to synthesize")
output_path: Optional[Path] = None
with tempfile.TemporaryDirectory(prefix="tts_parts_") as parts_dir, \
- self._chunk_heartbeat(
- chunk_num,
- label=None if self.chunk_text else "Request"):
+ self._chunk_heartbeat(chunk_num):
part_paths = []
for sub_num, sub_text in enumerate(sub_texts, 1):
wav = self._request_wav_with_retry(