aboutsummaryrefslogtreecommitdiff
path: root/converter
diff options
context:
space:
mode:
Diffstat (limited to 'converter')
-rw-r--r--converter/chunking.py30
-rw-r--r--converter/config.py9
-rw-r--r--converter/tts.py12
3 files changed, 22 insertions, 29 deletions
diff --git a/converter/chunking.py b/converter/chunking.py
index 1ce69a3..800b76a 100644
--- a/converter/chunking.py
+++ b/converter/chunking.py
@@ -2,28 +2,21 @@
import logging
import re
-from typing import List
+from typing import List, Optional
from . import config
logger = logging.getLogger(__name__)
-# Hard ceiling on words per request, regardless of the configured chunk
-# size. Both TTS servers silently truncate audio when a single generation
-# exceeds its cap (~2.5 min for the faster backend's static KV cache,
-# ~11 min for the Gradio demo) without reporting any error, so larger
-# requests are always split client-side. Keep a margin below ~300 words
-# to survive slow narration on the faster backend.
-MAX_REQUEST_WORDS = 250
-
-def split_into_chunks(text: str, max_words: int = config.CHUNK_SIZE) -> List[str]:
+def split_into_chunks(text: str, max_words: Optional[int] = None) -> List[str]:
"""Split text into chunks of at most ``max_words`` words.
- ``max_words`` is clamped to ``MAX_REQUEST_WORDS``: requests
- beyond that ceiling are silently truncated by the TTS servers (no
- error is reported), so chunks larger than the ceiling are never
- produced regardless of configuration.
+ ``max_words`` defaults to ``config.CHUNK_SIZE`` (read at call time).
+ There is no ceiling beyond that setting, but note that the TTS
+ servers silently truncate audio when a single generation runs too
+ long without reporting an error, so very large values are at your
+ own risk (see CHUNK_SIZE in converter/config.py).
Splits on sentence boundaries. Sentences longer than the limit are
split further at clause punctuation (which is kept attached for TTS
@@ -33,13 +26,8 @@ def split_into_chunks(text: str, max_words: int = config.CHUNK_SIZE) -> List[str
at word boundaries as a last resort: individual tokens stay intact,
but whitespace between them is normalized.
"""
- if max_words > MAX_REQUEST_WORDS:
- logger.warning(
- "Requested chunk size of %d words exceeds the %d-word request ceiling; "
- "larger requests are silently truncated by the TTS servers, so the "
- "size is clamped to %d words (see MAX_REQUEST_WORDS in converter/chunking.py)",
- max_words, MAX_REQUEST_WORDS, MAX_REQUEST_WORDS)
- max_words = MAX_REQUEST_WORDS
+ if max_words is None:
+ max_words = config.CHUNK_SIZE
if max_words < 1:
max_words = 1
diff --git a/converter/config.py b/converter/config.py
index 4cb5ab8..ed49f12 100644
--- a/converter/config.py
+++ b/converter/config.py
@@ -8,8 +8,13 @@ MAX_RETRIES = 3 # Attempts per chunk request
HEARTBEAT_INTERVAL_SECONDS = 30 # Print "still working" in console logs every N seconds
# Words per TTS generation request.
-# Note that qwen-tts-demo does no chunking at all, but faster-qwen-tts and
+# Note that qwen-tts-demo does no chunking at all, but faster-qwen3-tts and
# audio.cpp may do chunking as well, so you may be needlessly double-chunking.
+# This is the only size limit: there is no hard ceiling. However, servers
+# silently truncate audio when a single generation runs too long (roughly
+# ~2.5 min on the faster backend's static KV cache, ~11 min on the Gradio
+# demo) without reporting an error, so raising this is at your own risk.
+# The client-side truncation check still catches and retries gross cases.
CHUNK_SIZE = 250
# Default TTS backend.
@@ -54,5 +59,5 @@ FASTER_VOICE = "default"
AUDIOCPP_API_URL = "http://127.0.0.1:8080" # audio.cpp audiocpp_server
# Model ids in the audio.cpp server.json config.
-AUDIOCPP_MODEL_ID = "qwen"https://github.com/0xShug0/audio.cpp
+AUDIOCPP_MODEL_ID = "qwen"
AUDIOCPP_CLONE_MODEL_ID = "qwen-clone"
diff --git a/converter/tts.py b/converter/tts.py
index 741d9d3..a1c0b09 100644
--- a/converter/tts.py
+++ b/converter/tts.py
@@ -28,7 +28,7 @@ from typing import Any, Dict, List, Optional, Tuple
from . import config
from .audio import concat_audio_files, probe_duration_ms
-from .chunking import MAX_REQUEST_WORDS, split_into_chunks
+from .chunking import split_into_chunks
logger = logging.getLogger(__name__)
@@ -440,14 +440,14 @@ class QwenTTSClient(_BaseTTSClient):
"""Generate one audio chunk; returns its path in the chunks folder.
The text is split into sub-requests of at most
- ``MAX_REQUEST_WORDS`` words each (the book-level chunker
+ ``config.CHUNK_SIZE`` words each (the book-level chunker
normally guarantees this already; the split is defense in depth
against pathological input such as a punctuation-free run of
text), and the audio files returned for the sub-requests are
concatenated into one chunk file.
"""
try:
- sub_texts = split_into_chunks(text, max_words=MAX_REQUEST_WORDS)
+ sub_texts = split_into_chunks(text, max_words=config.CHUNK_SIZE)
if not sub_texts:
raise RuntimeError("No text to synthesize")
@@ -673,7 +673,7 @@ class FasterTTSClient(_BaseTTSClient):
def generate_chunk(self, text: str, chunk_num: int) -> Optional[str]:
"""Generate one audio chunk; returns its path in the chunks folder."""
try:
- sub_chunks = split_into_chunks(text, max_words=MAX_REQUEST_WORDS)
+ sub_chunks = split_into_chunks(text, max_words=config.CHUNK_SIZE)
if not sub_chunks:
raise RuntimeError("No text to synthesize")
@@ -914,13 +914,13 @@ class AudioCppTTSClient(_BaseTTSClient):
"""Generate one audio chunk; returns its path in the chunks folder.
The text is split into sub-requests of at most
- ``MAX_REQUEST_WORDS`` words each (defense in depth against
+ ``config.CHUNK_SIZE`` words each (defense in depth against
pathological input, matching the Gradio client), each sub-request
returns a complete WAV file, and the parts are concatenated into
one chunk file.
"""
try:
- sub_texts = split_into_chunks(text, max_words=MAX_REQUEST_WORDS)
+ sub_texts = split_into_chunks(text, max_words=config.CHUNK_SIZE)
if not sub_texts:
raise RuntimeError("No text to synthesize")