Make speech streaming default to SSE (#4679)

Signed-off-by: syd520zy <529477025@qq.com>
This commit is contained in:
faceless void authored and GitHub committed 2026-06-30 02:17:24 +00:00
1 parent c45ac74d43
commit 11f6a0b72e
39 files changed
+146 -58

No files matched your search

@@ -79,7 +79,7 @@ response = client.audio.speech.create(
response.stream_to_file("output.wav")
```
Streaming PCM output (where supported) — set `stream=true` with `response_format="pcm"`:
Streaming PCM output (where supported) — set `stream=true`, `stream_format="audio"`, and `response_format="pcm"`:
```bash
curl -X POST http://localhost:8091/v1/audio/speech \
@@ -88,6 +88,7 @@ curl -X POST http://localhost:8091/v1/audio/speech \
"input": "Hello, how are you?",
"voice": "default",
"stream": true,
"stream_format": "audio",
"response_format": "pcm"
}' --no-buffer | play -t raw -r 24000 -e signed -b 16 -c 1 -
```
@@ -363,6 +364,7 @@ curl -X POST http://localhost:8091/v1/audio/speech \
\"input\": \"Hello, streaming output from MOSS-TTS-Nano.\",
\"ref_audio\": \"data:audio/wav;base64,${REF_AUDIO}\",
\"stream\": true,
\"stream_format\": \"audio\",
\"response_format\": \"pcm\"
}" --no-buffer | play -t raw -r 48000 -e signed -b 16 -c 1 -
```
@@ -528,10 +530,11 @@ curl -X POST http://localhost:8091/v1/audio/speech \
"voice": "vivian",
"language": "English",
"stream": true,
"stream_format": "audio",
"response_format": "pcm"
}' --no-buffer | play -t raw -r 24000 -e signed -b 16 -c 1 -
```
Streaming requires `response_format="pcm"` and `async_chunk: true` on the stage config (default in `qwen3_tts.yaml`). `speed` is not supported when streaming.
Raw PCM streaming requires `stream_format="audio"`, `response_format="pcm"`, and `async_chunk: true` on the stage config (default in `qwen3_tts.yaml`). `speed` is not supported when streaming.
### Streaming WebSocket
The `/v1/audio/speech/stream` endpoint accepts text incrementally, splits it at sentence boundaries, and emits one PCM stream per sentence:
@@ -632,7 +635,7 @@ After startup, `/v1/audio/voices` lists `alice`, and `/v1/audio/speech` can use
```bash
python voxcpm2/gradio_demo.py
```
Uses an AudioWorklet-based player adapted from the Qwen3-TTS demo for gap-free playback. Audio is streamed from the OpenAI Speech endpoint with `stream=true`.
Uses an AudioWorklet-based player adapted from the Qwen3-TTS demo for gap-free playback. Raw PCM audio is streamed from the OpenAI Speech endpoint with `stream=true` and `stream_format="audio"`.
---
@@ -60,6 +60,7 @@ def run_tts(args) -> None:
if args.stream:
payload["stream"] = True
payload["stream_format"] = "audio"
payload["response_format"] = "pcm"
print(f"Model: {args.model}")
@@ -66,6 +66,8 @@ def build_payload(
"response_format": "pcm" if stream else response_format,
"stream": stream,
}
if stream:
payload["stream_format"] = "audio"
# Voice cloning: ref_audio takes priority over URL.
ref_url_stripped = ref_audio_url.strip() if ref_audio_url else ""
@@ -53,6 +53,7 @@ def run_tts(args) -> None:
if args.stream:
payload["stream"] = True
payload["stream_format"] = "audio"
payload["response_format"] = "pcm"
print(f"Model: {args.model}")
@@ -64,6 +64,8 @@ def build_payload(
"stream": stream,
"max_new_tokens": max_new_tokens,
}
if stream:
payload["stream_format"] = "audio"
# Voice cloning: ref_audio takes priority over URL.
ref_url_stripped = ref_audio_url.strip() if ref_audio_url else ""
@@ -40,6 +40,9 @@ def run_tts_generation(args) -> None:
"ref_audio": args.ref_audio,
"ref_text": args.ref_text,
}
if args.stream:
payload["stream_format"] = "audio"
payload["response_format"] = "pcm"
if args.max_new_tokens:
payload["max_new_tokens"] = args.max_new_tokens
@@ -123,6 +123,7 @@ def run_tts(args) -> None:
payload["max_new_tokens"] = args.max_new_tokens
if args.stream:
payload["stream"] = True
payload["stream_format"] = "audio"
payload["response_format"] = "pcm"
api_url = f"{args.api_base}/v1/audio/speech"
@@ -65,6 +65,7 @@ PY
\"model\": \"${MODEL}\",
\"input\": \"${TEXT}\",
\"stream\": true,
\"stream_format\": \"audio\",
\"response_format\": \"pcm\"
}" "$STREAM_OUTPUT"
;;
@@ -335,6 +335,7 @@ def build_payload(
"ref_audio": encode_audio_to_base64(ref_audio),
"response_format": "pcm" if stream else response_format,
"stream": stream,
**({"stream_format": "audio"} if stream else {}),
}
@@ -93,6 +93,8 @@ def build_payload(
"response_format": "pcm" if stream else response_format,
"stream": stream,
}
if stream:
payload["stream_format"] = "audio"
if not stream:
payload["speed"] = speed
@@ -515,6 +515,8 @@ def create_app(api_base: str):
"response_format": "pcm" if stream_enabled else "wav",
"stream": stream_enabled,
}
if stream_enabled:
payload["stream_format"] = "audio"
# Reference audio for cloning
ref_url_s = ref_url.strip() if ref_url else ""