Update app.py
Browse files
app.py
CHANGED
|
@@ -159,8 +159,10 @@ async def tts_say_stream_wav(
|
|
| 159 |
write_event({"type":"tts_stream_get","len":len(text),"voice":voice,"ls":ls})
|
| 160 |
|
| 161 |
async def gen():
|
| 162 |
-
|
| 163 |
-
|
|
|
|
|
|
|
| 164 |
# init
|
| 165 |
await ws.send(json.dumps({
|
| 166 |
"event": "init",
|
|
@@ -170,11 +172,11 @@ async def tts_say_stream_wav(
|
|
| 170 |
"noise_w": noise_w,
|
| 171 |
}))
|
| 172 |
sr, ch = 22050, 1
|
| 173 |
-
# wait for ready -> send header
|
| 174 |
while True:
|
| 175 |
m = await ws.recv()
|
| 176 |
if isinstance(m, (bytes, bytearray)):
|
| 177 |
-
#
|
| 178 |
continue
|
| 179 |
try:
|
| 180 |
evt = json.loads(m)
|
|
@@ -196,20 +198,43 @@ async def tts_say_stream_wav(
|
|
| 196 |
while True:
|
| 197 |
try:
|
| 198 |
msg = await ws.recv()
|
| 199 |
-
except websockets.exceptions.
|
|
|
|
|
|
|
| 200 |
break
|
|
|
|
| 201 |
if isinstance(msg, (bytes, bytearray)):
|
| 202 |
-
yield
|
| 203 |
-
|
| 204 |
-
|
| 205 |
-
|
| 206 |
-
|
| 207 |
-
|
| 208 |
-
|
| 209 |
-
|
| 210 |
-
|
| 211 |
-
|
| 212 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 213 |
|
| 214 |
return StreamingResponse(gen(), media_type="audio/wav",
|
| 215 |
headers={"Cache-Control":"no-cache","Connection":"keep-alive"})
|
|
|
|
| 159 |
write_event({"type":"tts_stream_get","len":len(text),"voice":voice,"ls":ls})
|
| 160 |
|
| 161 |
async def gen():
|
| 162 |
+
ws = None
|
| 163 |
+
try:
|
| 164 |
+
ws_url = _tts_ws_url()
|
| 165 |
+
ws = await websockets.connect(ws_url, ping_interval=None, max_size=8_000_000)
|
| 166 |
# init
|
| 167 |
await ws.send(json.dumps({
|
| 168 |
"event": "init",
|
|
|
|
| 172 |
"noise_w": noise_w,
|
| 173 |
}))
|
| 174 |
sr, ch = 22050, 1
|
| 175 |
+
# wait for ready -> send header immediately so client can start
|
| 176 |
while True:
|
| 177 |
m = await ws.recv()
|
| 178 |
if isinstance(m, (bytes, bytearray)):
|
| 179 |
+
# ignore stray audio until we know sr/ch
|
| 180 |
continue
|
| 181 |
try:
|
| 182 |
evt = json.loads(m)
|
|
|
|
| 198 |
while True:
|
| 199 |
try:
|
| 200 |
msg = await ws.recv()
|
| 201 |
+
except websockets.exceptions.ConnectionClosedOK:
|
| 202 |
+
break
|
| 203 |
+
except websockets.exceptions.ConnectionClosedError:
|
| 204 |
break
|
| 205 |
+
|
| 206 |
if isinstance(msg, (bytes, bytearray)):
|
| 207 |
+
# raw PCM16 frame from TTS; just yield
|
| 208 |
+
if msg:
|
| 209 |
+
yield msg
|
| 210 |
+
continue
|
| 211 |
+
|
| 212 |
+
# control event
|
| 213 |
+
try:
|
| 214 |
+
evt = json.loads(msg)
|
| 215 |
+
except Exception:
|
| 216 |
+
continue
|
| 217 |
+
kind = evt.get("event")
|
| 218 |
+
if kind in ("done", "end"):
|
| 219 |
+
break
|
| 220 |
+
if kind == "error":
|
| 221 |
+
# propagate as bytes (don’t raise; avoid chunk abort)
|
| 222 |
+
detail = evt.get("detail", "tts error")
|
| 223 |
+
yield f'ERROR: {detail}'.encode("utf-8")
|
| 224 |
+
break
|
| 225 |
+
# ignore logs
|
| 226 |
+
|
| 227 |
+
except Exception as e:
|
| 228 |
+
# Log, but don't raise (raising here aborts chunked stream & causes 'incomplete chunked read')
|
| 229 |
+
write_event({"type":"tts_stream_err","err":str(e)})
|
| 230 |
+
# a tiny trailing pad keeps some clients happy
|
| 231 |
+
yield b""
|
| 232 |
+
finally:
|
| 233 |
+
try:
|
| 234 |
+
if ws is not None:
|
| 235 |
+
await ws.close()
|
| 236 |
+
except Exception:
|
| 237 |
+
pass
|
| 238 |
|
| 239 |
return StreamingResponse(gen(), media_type="audio/wav",
|
| 240 |
headers={"Cache-Control":"no-cache","Connection":"keep-alive"})
|