multimodalart HF Staff commited on
Commit
7e2ebf9
·
verified ·
1 Parent(s): 4fc6256

Upload app.py with huggingface_hub

Browse files
Files changed (1) hide show
  1. app.py +69 -4
app.py CHANGED
@@ -11,6 +11,8 @@ from gradio import Server
11
  from fastapi import Request
12
  from fastapi.responses import HTMLResponse
13
  from huggingface_hub import hf_hub_download
 
 
14
 
15
  from magenta_rt import paths
16
 
@@ -145,9 +147,8 @@ async def banks(session_id: str = ""):
145
  return {"bankStatus": [os.path.exists(os.path.join(SESSION_DIR, f"{sid}_bank{i}.pt")) for i in range(3)]}
146
 
147
 
148
- @app.api(name="stream")
149
  @spaces.GPU(duration=90)
150
- def stream(session_id: str) -> str:
151
  """Continuous gen; switches model live when the dropdown changes."""
152
  from magenta_rt.torch.system import make_sampler, discretize_cfg, _float_to_int16, FRAME_SAMPLES
153
  if style_model.device != "cuda":
@@ -284,8 +285,8 @@ def stream(session_id: str) -> str:
284
  emitted_samples += audio.shape[1]
285
  frame_ms = (time.time() - gen_t) * 1000.0 / CHUNK # real per-frame inference time
286
  if audio.shape[1] > 0:
287
- b64 = base64.b64encode(_float_to_int16(audio[0].float().cpu().numpy()).astype("<i2").tobytes()).decode("ascii")
288
- yield f"{frame_ms:.1f}|{b64}"
289
  buf = (0.15, 0.3, 0.5)[max(0, min(2, int(c.get("buffer", 0))))]
290
  ahead = (emitted_samples / SR) - (time.time() - t0)
291
  if ahead < -0.3: # fell behind (cold-start/hiccup) -> resync, no catch-up burst
@@ -304,6 +305,70 @@ def stream(session_id: str) -> str:
304
  time.sleep(min(sleep_for, 0.5))
305
 
306
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
307
  @app.get("/")
308
  async def index():
309
  with open(os.path.join(os.path.dirname(os.path.abspath(__file__)), "index.html")) as f:
 
11
  from fastapi import Request
12
  from fastapi.responses import HTMLResponse
13
  from huggingface_hub import hf_hub_download
14
+ import asyncio, threading, queue as _q, contextvars, struct
15
+ from fastapi import WebSocket, WebSocketDisconnect
16
 
17
  from magenta_rt import paths
18
 
 
147
  return {"bankStatus": [os.path.exists(os.path.join(SESSION_DIR, f"{sid}_bank{i}.pt")) for i in range(3)]}
148
 
149
 
 
150
  @spaces.GPU(duration=90)
151
+ def gpu_stream(session_id):
152
  """Continuous gen; switches model live when the dropdown changes."""
153
  from magenta_rt.torch.system import make_sampler, discretize_cfg, _float_to_int16, FRAME_SAMPLES
154
  if style_model.device != "cuda":
 
285
  emitted_samples += audio.shape[1]
286
  frame_ms = (time.time() - gen_t) * 1000.0 / CHUNK # real per-frame inference time
287
  if audio.shape[1] > 0:
288
+ ab = _float_to_int16(audio[0].float().cpu().numpy()).astype("<i2").tobytes()
289
+ yield (ab, frame_ms)
290
  buf = (0.15, 0.3, 0.5)[max(0, min(2, int(c.get("buffer", 0))))]
291
  ahead = (emitted_samples / SR) - (time.time() - t0)
292
  if ahead < -0.3: # fell behind (cold-start/hiccup) -> resync, no catch-up burst
 
305
  time.sleep(min(sleep_for, 0.5))
306
 
307
 
308
+ _sessions = {}
309
+ _slock = threading.Lock()
310
+
311
+
312
+ def _worker(session_id, audio_q, stop_ev):
313
+ try:
314
+ while not stop_ev.is_set():
315
+ try:
316
+ for ab, fm in gpu_stream(session_id): # ~55s GPU grant; re-grants on loop
317
+ if stop_ev.is_set():
318
+ break
319
+ if audio_q.full():
320
+ try: audio_q.get_nowait()
321
+ except _q.Empty: pass
322
+ try: audio_q.put_nowait((ab, fm))
323
+ except _q.Full: pass
324
+ except Exception as e:
325
+ if "abort" in str(e).lower() or "duration" in str(e).lower():
326
+ continue # grant expired -> re-grant
327
+ print("[worker]", repr(e), flush=True); break
328
+ finally:
329
+ stop_ev.set()
330
+
331
+
332
+ @app.api(name="start")
333
+ def start(session_id: str = ""):
334
+ if not session_id:
335
+ return {"ok": False}
336
+ with _slock:
337
+ prev = _sessions.pop(session_id, None)
338
+ if prev:
339
+ prev["stop"].set()
340
+ audio_q = _q.Queue(maxsize=48); stop_ev = threading.Event()
341
+ ctx = contextvars.copy_context()
342
+ t = threading.Thread(target=ctx.run, args=(_worker, session_id, audio_q, stop_ev), daemon=True)
343
+ with _slock:
344
+ _sessions[session_id] = {"audio": audio_q, "stop": stop_ev}
345
+ t.start()
346
+ return {"ok": True}
347
+
348
+
349
+ @app.websocket("/ws")
350
+ async def audio_ws(websocket: WebSocket, session_id: str = ""):
351
+ await websocket.accept()
352
+ sess = _sessions.get(session_id)
353
+ if not sess:
354
+ await websocket.close(code=1008); return
355
+ audio_q, stop_ev = sess["audio"], sess["stop"]
356
+ loop = asyncio.get_event_loop()
357
+ try:
358
+ while not stop_ev.is_set():
359
+ try:
360
+ ab, fm = await loop.run_in_executor(None, lambda: audio_q.get(timeout=1.0))
361
+ except Exception:
362
+ continue
363
+ await websocket.send_bytes(struct.pack("<f", fm) + ab)
364
+ except WebSocketDisconnect:
365
+ pass
366
+ finally:
367
+ stop_ev.set()
368
+ with _slock:
369
+ _sessions.pop(session_id, None)
370
+
371
+
372
  @app.get("/")
373
  async def index():
374
  with open(os.path.join(os.path.dirname(os.path.abspath(__file__)), "index.html")) as f: