From 5ca77f86b70718b4ef1a07299efbd6431268d546 Mon Sep 17 00:00:00 2001 From: historia Date: Thu, 20 Aug 2026 18:06:22 -0400 Subject: fix: do not chunk with audio.cpp backend (double chunking) --- converter/converter.py | 40 ++++++++++++++++++++++++++++++++++++---- 1 file changed, 36 insertions(+), 4 deletions(-) (limited to 'converter/converter.py') 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 -- cgit v1.2.3