Upload app.py with huggingface_hub
Browse files
app.py
CHANGED
|
@@ -76,18 +76,11 @@ app = Server()
|
|
| 76 |
async def set_collider(request: Request):
|
| 77 |
body = await request.json()
|
| 78 |
sid = body["session_id"]
|
| 79 |
-
prompts = body.get("prompts") or ["instrumental music"]
|
| 80 |
-
weights = body.get("weights") or [1.0] * len(prompts)
|
| 81 |
-
s = float(sum(weights)) or 1.0
|
| 82 |
-
emb = np.zeros(style_model.embedding_dim, np.float32)
|
| 83 |
-
for label, w in zip(prompts, weights):
|
| 84 |
-
if w <= 0:
|
| 85 |
-
continue
|
| 86 |
-
emb += (w / s) * _embed(label)
|
| 87 |
-
tokens = style_model.tokenize(torch.from_numpy(emb)).tolist()
|
| 88 |
prev = read_slot(sid) or {}
|
| 89 |
write_slot(sid, {
|
| 90 |
-
"
|
|
|
|
|
|
|
| 91 |
"temperature": float(body.get("temperature", 1.1)),
|
| 92 |
"top_k": int(body.get("top_k", 50)),
|
| 93 |
"cfg_musiccoca": float(body.get("cfg", 1.6)),
|
|
@@ -102,7 +95,27 @@ async def set_collider(request: Request):
|
|
| 102 |
"reset": int(body.get("reset", 0)),
|
| 103 |
"seed": int(body.get("seed", 0)),
|
| 104 |
})
|
| 105 |
-
return {"ok": True
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 106 |
|
| 107 |
|
| 108 |
@app.api(name="stream")
|
|
@@ -127,6 +140,7 @@ def stream(session_id: str) -> str:
|
|
| 127 |
prev_active = set()
|
| 128 |
cur_reset = cur_seed = 0
|
| 129 |
force_reenc = False
|
|
|
|
| 130 |
while time.time() - t0 < 55.0:
|
| 131 |
c = read_slot(session_id)
|
| 132 |
if c is None:
|
|
@@ -146,17 +160,48 @@ def stream(session_id: str) -> str:
|
|
| 146 |
toks = []
|
| 147 |
for _ in range(10): # per-frame slot read -> steering applies ~instantly
|
| 148 |
c = read_slot(session_id) or c
|
| 149 |
-
|
|
|
|
|
|
|
| 150 |
active = set(c.get("notes") or [])
|
| 151 |
unmask = int(c.get("unmaskwidth", 0))
|
| 152 |
onsetmode = bool(c.get("onsetmode", False))
|
| 153 |
drumless = bool(c.get("drumless", False))
|
| 154 |
-
sig = (tuple(
|
|
|
|
|
|
|
| 155 |
if source is None or force_reenc or (sig != cur_sig and time.time() - last_enc >= 0.2):
|
| 156 |
onsets = active - prev_active
|
| 157 |
prev_active = set(active)
|
| 158 |
cur_sig, last_enc = sig, time.time()
|
| 159 |
-
force_reenc = onsetmode and bool(onsets)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 160 |
nvec = [] # per-pitch: -1 masked / 0 off / 1 cont / 2 onset / 3 on
|
| 161 |
for pitch in range(128):
|
| 162 |
if pitch in active:
|
|
|
|
| 76 |
async def set_collider(request: Request):
|
| 77 |
body = await request.json()
|
| 78 |
sid = body["session_id"]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 79 |
prev = read_slot(sid) or {}
|
| 80 |
write_slot(sid, {
|
| 81 |
+
"prompts": body.get("prompts") or ["instrumental music"],
|
| 82 |
+
"weights": body.get("weights") or [1.0],
|
| 83 |
+
"audio": body.get("audio") or [],
|
| 84 |
"temperature": float(body.get("temperature", 1.1)),
|
| 85 |
"top_k": int(body.get("top_k", 50)),
|
| 86 |
"cfg_musiccoca": float(body.get("cfg", 1.6)),
|
|
|
|
| 95 |
"reset": int(body.get("reset", 0)),
|
| 96 |
"seed": int(body.get("seed", 0)),
|
| 97 |
})
|
| 98 |
+
return {"ok": True}
|
| 99 |
+
|
| 100 |
+
|
| 101 |
+
@app.post("/audio")
|
| 102 |
+
async def set_audio(request: Request):
|
| 103 |
+
"""Store an uploaded clip (native-rate mono float32) per (session, slot);
|
| 104 |
+
the GPU stream embeds it with MusicCoCa.embed_audio (resampy -> 16kHz)."""
|
| 105 |
+
body = await request.json()
|
| 106 |
+
sid = os.path.basename(body["session_id"]); slot = int(body.get("slot", 0))
|
| 107 |
+
path = os.path.join(SESSION_DIR, f"{sid}_aud{slot}.bin")
|
| 108 |
+
samples = body.get("samples")
|
| 109 |
+
if not samples:
|
| 110 |
+
try: os.remove(path)
|
| 111 |
+
except OSError: pass
|
| 112 |
+
return {"ok": True}
|
| 113 |
+
raw = base64.b64decode(samples)
|
| 114 |
+
sr = int(body.get("sample_rate", 48000))
|
| 115 |
+
with open(path + ".tmp", "wb") as f:
|
| 116 |
+
f.write(int(sr).to_bytes(4, "little")); f.write(raw)
|
| 117 |
+
os.replace(path + ".tmp", path)
|
| 118 |
+
return {"ok": True}
|
| 119 |
|
| 120 |
|
| 121 |
@app.api(name="stream")
|
|
|
|
| 140 |
prev_active = set()
|
| 141 |
cur_reset = cur_seed = 0
|
| 142 |
force_reenc = False
|
| 143 |
+
txt_cache, aud_cache = {}, {}
|
| 144 |
while time.time() - t0 < 55.0:
|
| 145 |
c = read_slot(session_id)
|
| 146 |
if c is None:
|
|
|
|
| 160 |
toks = []
|
| 161 |
for _ in range(10): # per-frame slot read -> steering applies ~instantly
|
| 162 |
c = read_slot(session_id) or c
|
| 163 |
+
prompts = c.get("prompts") or ["instrumental music"]
|
| 164 |
+
weights = c.get("weights") or [1.0] * len(prompts)
|
| 165 |
+
aflags = (c.get("audio") or []) + [False] * len(prompts)
|
| 166 |
active = set(c.get("notes") or [])
|
| 167 |
unmask = int(c.get("unmaskwidth", 0))
|
| 168 |
onsetmode = bool(c.get("onsetmode", False))
|
| 169 |
drumless = bool(c.get("drumless", False))
|
| 170 |
+
sig = (tuple(prompts), tuple(round(float(w), 4) for w in weights),
|
| 171 |
+
tuple(bool(a) for a in aflags[:len(prompts)]),
|
| 172 |
+
tuple(sorted(active)), unmask, onsetmode, drumless)
|
| 173 |
if source is None or force_reenc or (sig != cur_sig and time.time() - last_enc >= 0.2):
|
| 174 |
onsets = active - prev_active
|
| 175 |
prev_active = set(active)
|
| 176 |
cur_sig, last_enc = sig, time.time()
|
| 177 |
+
force_reenc = onsetmode and bool(onsets)
|
| 178 |
+
# ---- style tokens on GPU: embed (text+audio), weight-blend, tokenize ----
|
| 179 |
+
emb = torch.zeros(style_model.embedding_dim, device=dev, dtype=torch.float32)
|
| 180 |
+
tot = float(sum(w for w in weights if w > 0)) or 1.0
|
| 181 |
+
for i in range(len(prompts)):
|
| 182 |
+
w = float(weights[i]) if i < len(weights) else 0.0
|
| 183 |
+
if w <= 0:
|
| 184 |
+
continue
|
| 185 |
+
if i < len(aflags) and aflags[i]:
|
| 186 |
+
ap = os.path.join(SESSION_DIR, f"{os.path.basename(session_id)}_aud{i}.bin")
|
| 187 |
+
try: mt = os.path.getmtime(ap)
|
| 188 |
+
except OSError: continue
|
| 189 |
+
ce = aud_cache.get(i)
|
| 190 |
+
if ce is None or ce[0] != mt:
|
| 191 |
+
data = open(ap, "rb").read()
|
| 192 |
+
asr = int.from_bytes(data[:4], "little")
|
| 193 |
+
samp = np.frombuffer(data[4:], dtype=np.float32)
|
| 194 |
+
ce = (mt, style_model.embed_audio(samp, asr).float()); aud_cache[i] = ce
|
| 195 |
+
e = ce[1]
|
| 196 |
+
else:
|
| 197 |
+
lbl = (prompts[i] or "").strip()
|
| 198 |
+
if not lbl:
|
| 199 |
+
continue
|
| 200 |
+
e = txt_cache.get(lbl)
|
| 201 |
+
if e is None:
|
| 202 |
+
e = style_model.embed(lbl).float(); txt_cache[lbl] = e
|
| 203 |
+
emb = emb + (w / tot) * e
|
| 204 |
+
tokens = style_model.tokenize(emb).tolist() # onset(2)->continuation(1) on the next frame
|
| 205 |
nvec = [] # per-pitch: -1 masked / 0 off / 1 cont / 2 onset / 3 on
|
| 206 |
for pitch in range(128):
|
| 207 |
if pitch in active:
|