Add debug logging to synthesis pipeline to trace hang
All checks were successful
Build and Push Docker Image / build (push) Successful in 2m11s

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-04-08 19:34:18 -04:00
parent 8272b6a8c9
commit 067a853d3b

View File

@@ -119,32 +119,40 @@ class KokoroEventHandler(AsyncEventHandler):
try: try:
chunk_queue: asyncio.Queue = asyncio.Queue() chunk_queue: asyncio.Queue = asyncio.Queue()
loop = asyncio.get_event_loop() loop = asyncio.get_running_loop()
def _generate(): def _generate():
chunk_count = 0
try: try:
_LOGGER.debug("Pipeline thread started")
for _, _, audio in self.pipeline(text, voice=voice_name, speed=speed): for _, _, audio in self.pipeline(text, voice=voice_name, speed=speed):
if audio is None: if audio is None:
continue continue
# float32 [-1, 1] → int16 # float32 [-1, 1] → int16
audio_np = audio.cpu().numpy() if hasattr(audio, 'cpu') else audio audio_np = audio.cpu().numpy() if hasattr(audio, 'cpu') else audio
pcm = (np.clip(audio_np, -1.0, 1.0) * 32767).astype(np.int16) pcm = (np.clip(audio_np, -1.0, 1.0) * 32767).astype(np.int16)
asyncio.run_coroutine_threadsafe( chunk_count += 1
chunk_queue.put(pcm.tobytes()), loop _LOGGER.debug("Queueing chunk %d (%d bytes)", chunk_count, len(pcm.tobytes()))
) fut = asyncio.run_coroutine_threadsafe(chunk_queue.put(pcm.tobytes()), loop)
fut.result() # propagate any queue errors
_LOGGER.debug("Pipeline finished, %d chunks generated", chunk_count)
except Exception as exc: except Exception as exc:
asyncio.run_coroutine_threadsafe(chunk_queue.put(exc), loop) _LOGGER.exception("Pipeline thread error")
asyncio.run_coroutine_threadsafe(chunk_queue.put(exc), loop).result()
finally: finally:
asyncio.run_coroutine_threadsafe(chunk_queue.put(None), loop) asyncio.run_coroutine_threadsafe(chunk_queue.put(None), loop).result()
self.executor.submit(_generate) self.executor.submit(_generate)
chunks_sent = 0
while True: while True:
item = await chunk_queue.get() item = await chunk_queue.get()
if item is None: if item is None:
break break
if isinstance(item, Exception): if isinstance(item, Exception):
raise item raise item
chunks_sent += 1
_LOGGER.debug("Sending audio chunk %d", chunks_sent)
await self.write_event( await self.write_event(
AudioChunk( AudioChunk(
rate=KOKORO_SAMPLE_RATE, rate=KOKORO_SAMPLE_RATE,
@@ -153,6 +161,7 @@ class KokoroEventHandler(AsyncEventHandler):
audio=item, audio=item,
).event() ).event()
) )
_LOGGER.info("Synthesis complete, sent %d chunks", chunks_sent)
except Exception: except Exception:
_LOGGER.exception("Error during synthesis") _LOGGER.exception("Error during synthesis")
finally: finally:
@@ -168,7 +177,7 @@ async def main() -> None:
args = parser.parse_args() args = parser.parse_args()
logging.basicConfig( logging.basicConfig(
level=logging.DEBUG if args.debug else logging.INFO, level=logging.DEBUG,
format="%(asctime)s %(levelname)s %(name)s: %(message)s", format="%(asctime)s %(levelname)s %(name)s: %(message)s",
) )