Funktioniert

This commit is contained in:
2026-06-18 17:27:42 +02:00
parent 94cbf29972
commit 569970e339
20 changed files with 2199 additions and 88 deletions

View File

@@ -6,6 +6,7 @@ import time
import wave
import struct
import asyncio
import re
try:
import websockets
@@ -18,12 +19,30 @@ except ImportError:
WS_URL = "ws://localhost:8091/v1/audio/speech/stream"
OUTPUT_FILE = "test_stream_output.wav"
SAMPLE_RATE = 24000
WORD_RE = re.compile(r"\S+\s*")
async def stream_tts(text: str, voice: str = "Ryan", language: str = "German"):
def iter_words(text: str):
"""Yield complete words, preserving trailing whitespace.
The WebSocket client must not forward raw LLM tokens such as partial
subwords. It buffers to words and lets the server detect sentence ends.
"""
for match in WORD_RE.finditer(text):
yield match.group(0)
async def stream_tts(
text: str,
voice: str = "Ryan",
language: str = "German",
instructions: str = "Eine warme, ruhige deutsche Stimme, klar artikuliert, freundlich und natuerlich.",
seed: int = 42,
):
print(f"[→] WebSocket connect: {WS_URL}")
print(f" text: {text!r}")
print(f" voice: {voice}, language: {language}")
print(f" voice: {voice}, language: {language}, seed: {seed}")
print(f" instruct: {instructions!r}")
pcm_chunks = []
total_bytes = 0
@@ -32,21 +51,28 @@ async def stream_tts(text: str, voice: str = "Ryan", language: str = "German"):
async with websockets.connect(WS_URL) as ws:
# Send session config
await ws.send(json.dumps({
config = {
"type": "session.config",
"model": "Qwen/Qwen3-TTS-12Hz-1.7B-CustomVoice",
"task_type": "CustomVoice",
"task_type": "VoiceDesign",
"voice": voice,
"language": language,
"instructions": instructions,
"seed": seed,
"response_format": "pcm",
"stream_audio": True,
}))
"split_granularity": "sentence",
}
print("[cfg]", json.dumps(config, ensure_ascii=False, sort_keys=True), flush=True)
await ws.send(json.dumps(config))
# Send text
await ws.send(json.dumps({
"type": "input.text",
"text": text,
}))
# Send word-by-word. Do not split into sentences client-side; the
# server still decides when a complete sentence is ready for audio.
for word in iter_words(text):
await ws.send(json.dumps({
"type": "input.text",
"text": word,
}))
await ws.send(json.dumps({"type": "input.done"}))
# Receive