aboutsummaryrefslogtreecommitdiff
path: root/converter/converter.py
diff options
context:
space:
mode:
authorhistoria <historiavg@proton.me>2026-08-20 18:06:22 -0400
committerhistoria <historiavg@proton.me>2026-08-20 18:06:22 -0400
commit5ca77f86b70718b4ef1a07299efbd6431268d546 (patch)
treee50a439ea7a92bd628f78240fdbc2d4093df6928 /converter/converter.py
parentb873844f7eb681119542661ef588c5b452f88763 (diff)
downloadtts-audiobook-generator-5ca77f86b70718b4ef1a07299efbd6431268d546.tar.gz
fix: do not chunk with audio.cpp backend (double chunking)
Diffstat (limited to 'converter/converter.py')
-rw-r--r--converter/converter.py40
1 files changed, 36 insertions, 4 deletions
diff --git a/converter/converter.py b/converter/converter.py
index 6994a31..f1064f1 100644
--- a/converter/converter.py
+++ b/converter/converter.py
@@ -134,7 +134,8 @@ class AudiobookConverter:
voice_clone_ref_text: Optional[str] = None, skip_transcription: bool = False,
speed: float = 1.0, single_file: bool = False, output_format: str = config.AUDIO_FORMAT,
language: Optional[str] = None, backend: str = BACKEND_GRADIO,
- voice: Optional[str] = None, debug: bool = False):
+ voice: Optional[str] = None, debug: bool = False,
+ chunk: bool = False):
if speed <= 0:
raise ValueError(f"Speed must be a positive number, got {speed}")
if output_format not in AUDIO_FORMATS:
@@ -154,6 +155,12 @@ class AudiobookConverter:
self.backend = backend
self.voice = voice
self.debug = bool(debug)
+ # Client-side chunking: the gradio and faster backends always chunk
+ # (their servers do one generation per request and silently truncate
+ # long text). The audio.cpp server chunks long text itself, so it
+ # defaults to one request per chapter; --chunk forces client-side
+ # chunking on top (possible needless double-chunking).
+ self.client_chunks = bool(chunk) or backend != BACKEND_AUDIOCPP
self._validate_configuration()
if backend == BACKEND_FASTER:
# The faster backend always voice-clones using a reference voice
@@ -162,7 +169,8 @@ class AudiobookConverter:
elif backend == BACKEND_AUDIOCPP:
# Speaker mode (no voice) uses a built-in CustomVoice speaker;
# an explicit voice selects a server-side preset (cloning).
- self.tts = AudioCppTTSClient(voice=voice, language=self.language)
+ self.tts = AudioCppTTSClient(voice=voice, language=self.language,
+ chunk_text=self.client_chunks)
else:
self.tts = QwenTTSClient(
voice_mode=voice_mode,
@@ -426,6 +434,18 @@ class AudiobookConverter:
logger.info("Chunk processing completed: %d/%d chunks", successful_chunks, total_chunks)
return results
+ def _chapter_chunks(self, text: str) -> List[str]:
+ """Split chapter text into TTS requests.
+
+ Client-side chunking splits into CHUNK_SIZE-word chunks (gradio and
+ faster always; audio.cpp only with --chunk). Otherwise (audio.cpp
+ default) the whole text is one request and the server does its own
+ long-form chunking.
+ """
+ if self.client_chunks:
+ return chunking.split_into_chunks(text)
+ return [text] if text.strip() else []
+
def _convert_text(self, text: str, output_path: Path, start_time: float,
speed: Optional[float] = None,
output_format: Optional[str] = None,
@@ -452,7 +472,7 @@ class AudiobookConverter:
logger.info("Extracted %d characters (%d words)", len(text), len(text.split()))
- chunks = chunking.split_into_chunks(text)
+ chunks = self._chapter_chunks(text)
total_chunks = len(chunks)
if total_chunks == 0:
logger.error("No chunks created")
@@ -460,7 +480,13 @@ class AudiobookConverter:
chunk_sizes = [len(chunk.split()) for chunk in chunks]
avg_chunk_size = sum(chunk_sizes) / len(chunk_sizes)
- logger.info("Split into %d chunks (avg %.0f words per chunk)", total_chunks, avg_chunk_size)
+ if len(chunks) == 1:
+ logger.info("Sending the whole text as one request (%d words; "
+ "the server chunks long text itself)",
+ chunk_sizes[0])
+ else:
+ logger.info("Split into %d chunks (avg %.0f words per chunk)",
+ total_chunks, avg_chunk_size)
backend_labels = {
BACKEND_FASTER: "faster TTS API",
BACKEND_AUDIOCPP: "audio.cpp server",
@@ -526,6 +552,12 @@ class AudiobookConverter:
else:
print("Backend: audio.cpp (custom voice, built-in speaker)")
print(f"Speaker: {config.SPEAKER}")
+ if self.client_chunks:
+ print("Chunking: client-side (--chunk; the server also chunks "
+ "long text itself, so this may double-chunk)")
+ else:
+ print("Chunking: server-side (one request per chapter; "
+ "--chunk forces client-side chunking)")
print(f"Language: {self.language}")
else:
api_url = (config.CLONE_API_URL if self.voice_mode == VOICE_MODE_CLONE