aspirant312 commited on
Commit
0d8df70
·
1 Parent(s): f9c6021

Add WebSocket endpoint for true real-time bidirectional streaming

Browse files
Files changed (1) hide show
  1. api.py +81 -5
api.py CHANGED
@@ -1,5 +1,6 @@
1
- from fastapi import FastAPI, HTTPException
2
  from fastapi.responses import StreamingResponse
 
3
  import torch
4
  import numpy as np
5
  import re
@@ -179,10 +180,13 @@ async def root():
179
  "speakers": list(SPEAKERS.keys()),
180
  "sample_rate": SAMPLE_RATE,
181
  "endpoints": {
182
- "/tts": "Standard TTS (wait for full audio)",
183
- "/tts/stream": "Streaming TTS (audio chunks in real-time - recommended for Pipecat)",
184
- "/speakers": "List available speakers"
185
- }
 
 
 
186
  }
187
 
188
 
@@ -375,6 +379,78 @@ async def get_speakers():
375
  return {"speakers": list(SPEAKERS.keys())}
376
 
377
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
378
  if __name__ == "__main__":
379
  import uvicorn
380
  uvicorn.run(app, host="0.0.0.0", port=7860)
 
1
+ from fastapi import FastAPI, HTTPException, WebSocket, WebSocketDisconnect
2
  from fastapi.responses import StreamingResponse
3
+ import json
4
  import torch
5
  import numpy as np
6
  import re
 
180
  "speakers": list(SPEAKERS.keys()),
181
  "sample_rate": SAMPLE_RATE,
182
  "endpoints": {
183
+ "POST /tts": "Standard TTS (wait for full audio)",
184
+ "POST /tts/stream": "HTTP Streaming TTS (audio chunks in real-time)",
185
+ "WS /ws/tts": "WebSocket TTS (BEST FOR PIPECAT - true real-time bidirectional)",
186
+ "GET /speakers": "List available speakers"
187
+ },
188
+ "device": DEVICE,
189
+ "optimization": "fp16 (half precision)"
190
  }
191
 
192
 
 
379
  return {"speakers": list(SPEAKERS.keys())}
380
 
381
 
382
+ @app.websocket("/ws/tts")
383
+ async def websocket_tts(websocket: WebSocket):
384
+ """WebSocket endpoint for real-time audio streaming.
385
+
386
+ Usage:
387
+ 1. Connect to ws://localhost:7860/ws/tts
388
+ 2. Send JSON: {"text": "سلام دنیا", "speaker": "Divya"}
389
+ 3. Receive audio chunks in real-time
390
+ 4. Connection closes when generation completes
391
+ """
392
+ await websocket.accept()
393
+ try:
394
+ data = await websocket.receive_text()
395
+ request_data = json.loads(data)
396
+
397
+ text = request_data.get("text", "").strip()
398
+ speaker = request_data.get("speaker", "Divya")
399
+ pitch = request_data.get("pitch", "Moderate")
400
+ rate = request_data.get("rate", "Moderate")
401
+ temperature = request_data.get("temperature", 0.8)
402
+ do_sample = request_data.get("do_sample", True)
403
+
404
+ # Validate inputs
405
+ if not text:
406
+ await websocket.send_json({"error": "Text cannot be empty"})
407
+ await websocket.close()
408
+ return
409
+
410
+ if speaker not in SPEAKERS:
411
+ await websocket.send_json({
412
+ "error": f"Invalid speaker. Choose from: {list(SPEAKERS.keys())}"
413
+ })
414
+ await websocket.close()
415
+ return
416
+
417
+ # Send status message
418
+ await websocket.send_json({
419
+ "status": "generating",
420
+ "message": f"Generating audio for speaker {speaker}..."
421
+ })
422
+
423
+ # Generate and stream audio chunks
424
+ chunk_count = 0
425
+ try:
426
+ for audio_chunk in generate_audio_chunks_streaming(
427
+ text, speaker, pitch, rate, temperature, do_sample
428
+ ):
429
+ # Send audio chunk as binary data
430
+ await websocket.send_bytes(audio_chunk.tobytes())
431
+ chunk_count += 1
432
+
433
+ # Send completion message
434
+ await websocket.send_json({
435
+ "status": "complete",
436
+ "chunks_sent": chunk_count
437
+ })
438
+
439
+ except Exception as e:
440
+ await websocket.send_json({
441
+ "error": f"Generation failed: {str(e)}"
442
+ })
443
+
444
+ except WebSocketDisconnect:
445
+ print("WebSocket client disconnected")
446
+ except json.JSONDecodeError:
447
+ await websocket.send_json({"error": "Invalid JSON format"})
448
+ await websocket.close()
449
+ except Exception as e:
450
+ print(f"WebSocket error: {e}")
451
+ await websocket.close()
452
+
453
+
454
  if __name__ == "__main__":
455
  import uvicorn
456
  uvicorn.run(app, host="0.0.0.0", port=7860)