Spaces:
Running on Zero
Running on Zero
Vansh Chugh commited on
Commit ·
bb25b43
1
Parent(s): 7b11b09
Drop gpu_lock: Gradio's default concurrency_limit=1 already serializes process_fn
Browse files
app.py
CHANGED
|
@@ -62,20 +62,11 @@ VARIANTS = {
|
|
| 62 |
},
|
| 63 |
}
|
| 64 |
|
| 65 |
-
# Only one variant
|
| 66 |
-
# time: the two variants need different Qwen2.5-Omni-7B layer-sampling depths, so
|
| 67 |
-
# the encoder can't be shared as-is (QwenOmniThinkerExtractor bakes dit_depth into
|
| 68 |
-
# which LLM layers it extracts at construction time). Switching variants evicts the
|
| 69 |
-
# previous one and rebuilds from the local HF cache (no re-download).
|
| 70 |
_active_name = None
|
| 71 |
_active_entry = None # built on CPU at first; moved to GPU on first real use
|
| 72 |
_loading = True
|
| 73 |
_load_error = None
|
| 74 |
-
|
| 75 |
-
# Only one request runs at a time. The scheduler object is shared and stateful,
|
| 76 |
-
# so two requests running together would corrupt each other's results. This lock
|
| 77 |
-
# also covers _active_name/_active_entry below, so no separate lock is needed.
|
| 78 |
-
_gpu_lock = threading.Lock()
|
| 79 |
_active_ready = False # has _active_entry been moved onto the GPU yet?
|
| 80 |
|
| 81 |
|
|
@@ -162,9 +153,8 @@ def get_variant(name: str):
|
|
| 162 |
"""Return the active variant's (model, extractor, vae, ...) tuple, ready to
|
| 163 |
use on the GPU — building and/or activating it first if needed.
|
| 164 |
|
| 165 |
-
|
| 166 |
-
what makes
|
| 167 |
-
touch CUDA in _activate_variant()."""
|
| 168 |
global _active_name, _active_entry, _active_ready
|
| 169 |
if name != _active_name:
|
| 170 |
print(f"Switching variant: {_active_name!r} -> {name!r}")
|
|
@@ -214,108 +204,107 @@ def process_fn(
|
|
| 214 |
with tempfile.NamedTemporaryFile(suffix=".wav", delete=False) as f:
|
| 215 |
out_path = f.name
|
| 216 |
|
| 217 |
-
|
| 218 |
-
|
| 219 |
-
|
| 220 |
-
|
| 221 |
-
|
| 222 |
-
|
| 223 |
-
|
| 224 |
-
|
| 225 |
-
|
| 226 |
-
|
| 227 |
-
|
| 228 |
-
|
| 229 |
-
|
| 230 |
-
|
| 231 |
-
|
| 232 |
-
|
| 233 |
-
|
| 234 |
-
|
| 235 |
-
|
| 236 |
-
|
| 237 |
-
|
| 238 |
-
|
| 239 |
-
|
| 240 |
-
|
| 241 |
-
|
| 242 |
-
|
| 243 |
-
|
| 244 |
-
|
| 245 |
-
|
| 246 |
-
|
| 247 |
-
|
| 248 |
-
|
| 249 |
-
|
| 250 |
-
|
| 251 |
-
|
| 252 |
-
|
| 253 |
-
|
| 254 |
-
|
| 255 |
-
|
| 256 |
-
|
| 257 |
-
|
| 258 |
-
|
| 259 |
-
|
| 260 |
-
|
| 261 |
-
|
| 262 |
-
|
| 263 |
-
|
| 264 |
-
|
| 265 |
-
|
| 266 |
-
|
| 267 |
-
|
| 268 |
-
|
| 269 |
-
|
| 270 |
-
|
| 271 |
-
|
| 272 |
-
|
| 273 |
-
|
| 274 |
-
|
| 275 |
-
|
| 276 |
-
|
| 277 |
-
|
| 278 |
-
|
| 279 |
-
|
| 280 |
-
|
| 281 |
-
|
| 282 |
-
|
| 283 |
-
|
| 284 |
-
|
| 285 |
-
|
| 286 |
-
|
| 287 |
-
|
| 288 |
-
|
| 289 |
-
|
| 290 |
-
|
| 291 |
-
|
| 292 |
-
|
| 293 |
-
|
| 294 |
-
|
| 295 |
-
|
| 296 |
-
|
| 297 |
-
|
| 298 |
-
|
| 299 |
-
|
| 300 |
-
|
| 301 |
-
|
| 302 |
-
|
| 303 |
-
|
| 304 |
-
|
| 305 |
-
|
| 306 |
-
|
| 307 |
-
|
| 308 |
-
|
| 309 |
-
|
| 310 |
-
|
| 311 |
-
|
| 312 |
-
|
| 313 |
-
|
| 314 |
-
|
| 315 |
-
|
| 316 |
-
|
| 317 |
-
|
| 318 |
-
raise gr.Error(f"Unknown mode: {mode}")
|
| 319 |
|
| 320 |
return out_path
|
| 321 |
|
|
|
|
| 62 |
},
|
| 63 |
}
|
| 64 |
|
| 65 |
+
# Only one variant is kept on the GPU at a time; switching evicts the other.
|
|
|
|
|
|
|
|
|
|
|
|
|
| 66 |
_active_name = None
|
| 67 |
_active_entry = None # built on CPU at first; moved to GPU on first real use
|
| 68 |
_loading = True
|
| 69 |
_load_error = None
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 70 |
_active_ready = False # has _active_entry been moved onto the GPU yet?
|
| 71 |
|
| 72 |
|
|
|
|
| 153 |
"""Return the active variant's (model, extractor, vae, ...) tuple, ready to
|
| 154 |
use on the GPU — building and/or activating it first if needed.
|
| 155 |
|
| 156 |
+
Must be called from inside process_fn (i.e. inside @spaces.GPU) — that's
|
| 157 |
+
what makes it safe to touch CUDA in _activate_variant()."""
|
|
|
|
| 158 |
global _active_name, _active_entry, _active_ready
|
| 159 |
if name != _active_name:
|
| 160 |
print(f"Switching variant: {_active_name!r} -> {name!r}")
|
|
|
|
| 204 |
with tempfile.NamedTemporaryFile(suffix=".wav", delete=False) as f:
|
| 205 |
out_path = f.name
|
| 206 |
|
| 207 |
+
model, omni_extractor, audio_vae, vae_scale_factor, vae_sample_rate, gen_target_frames, scheduler = get_variant(model_variant)
|
| 208 |
+
|
| 209 |
+
if mode == "Generate":
|
| 210 |
+
# Text-to-audio/speech, no input audio involved. sound_type picks which of
|
| 211 |
+
# the model's trained prompt templates to build (see README task table) —
|
| 212 |
+
# voice/background only apply to the Speech templates, unused otherwise.
|
| 213 |
+
if sound_type == "Sound Effect":
|
| 214 |
+
tagged_prompt = f"[Audio] {prompt}"
|
| 215 |
+
elif sound_type == "Speech":
|
| 216 |
+
tagged_prompt = f'[Speech] A {voice.lower()} voice saying "{prompt}"'
|
| 217 |
+
else: # "Speech + Background"
|
| 218 |
+
tagged_prompt = f'[Speech] A {voice.lower()} voice saying "{prompt}" [Audio] {background}'
|
| 219 |
+
|
| 220 |
+
latents = sample_latents(
|
| 221 |
+
model, scheduler, omni_extractor, [tagged_prompt],
|
| 222 |
+
num_inference_steps=steps, guidance_scale=guidance, device=DEVICE,
|
| 223 |
+
target_frames=gen_target_frames,
|
| 224 |
+
)
|
| 225 |
+
latents = latents * (1.0 / vae_scale_factor)
|
| 226 |
+
decode_and_save(audio_vae, latents, [duration], [out_path], sample_rate=vae_sample_rate)
|
| 227 |
+
|
| 228 |
+
elif mode == "Edit":
|
| 229 |
+
# Source audio + instruction. Mask is all-ones: the whole clip is editable,
|
| 230 |
+
# source_latents just gives the model something to condition on.
|
| 231 |
+
# sound_type again picks [Audio] vs [Speech] as the edit's sub-tag; "Speech
|
| 232 |
+
# + Background" isn't a real edit template, so it also falls back to [Audio].
|
| 233 |
+
if not input_audio_path:
|
| 234 |
+
raise gr.Error("Edit mode requires an input audio track.")
|
| 235 |
+
edit_target = "Speech" if sound_type == "Speech" else "Audio"
|
| 236 |
+
tgt_wav_len = int(MAX_AUDIO_DURATION * vae_sample_rate)
|
| 237 |
+
src_wav = load_source_audio(input_audio_path, target_sr=vae_sample_rate,
|
| 238 |
+
target_length=tgt_wav_len, device=DEVICE)
|
| 239 |
+
src_latent = audio_vae.wrapped_encode(src_wav) * vae_scale_factor
|
| 240 |
+
mask_wav = make_edit_mask(src_wav.shape[-1], DEVICE)
|
| 241 |
+
mask_lat = downsample_mask(mask_wav, src_latent.shape[-1])
|
| 242 |
+
|
| 243 |
+
latents = sample_latents(
|
| 244 |
+
model, scheduler, omni_extractor, [f"[Edit] [{edit_target}] {prompt}"],
|
| 245 |
+
source_latents=src_latent, masks=mask_lat,
|
| 246 |
+
num_inference_steps=steps, guidance_scale=guidance, device=DEVICE,
|
| 247 |
+
)
|
| 248 |
+
latents = latents * (1.0 / vae_scale_factor)
|
| 249 |
+
edit_duration = src_wav.shape[-1] / vae_sample_rate
|
| 250 |
+
decode_and_save(audio_vae, latents, [edit_duration], [out_path], sample_rate=vae_sample_rate)
|
| 251 |
+
|
| 252 |
+
elif mode == "Clone Voice":
|
| 253 |
+
# Reference clip + text to speak in that voice.
|
| 254 |
+
if not input_audio_path:
|
| 255 |
+
raise gr.Error("Clone Voice mode requires a reference audio track.")
|
| 256 |
+
|
| 257 |
+
# 3.0s matches REF_DURATION, the reference-clip length the model was trained with.
|
| 258 |
+
ref_wav = load_ref_audio(input_audio_path, target_sr=vae_sample_rate,
|
| 259 |
+
max_ref_duration=3.0, device=DEVICE)
|
| 260 |
+
ref_audio_duration = ref_wav.shape[-1] / vae_sample_rate
|
| 261 |
+
if ref_audio_duration + 1.0 > duration: # 1.0s floor so some cloned speech always fits
|
| 262 |
+
raise gr.Error(f"Reference clip ({ref_audio_duration:.1f}s) leaves under 1s for "
|
| 263 |
+
f"the cloned speech at Duration={duration:.1f}s. Increase Duration.")
|
| 264 |
+
|
| 265 |
+
# Get the ref transcript (typed one wins over auto-transcription), then
|
| 266 |
+
# combine it with what the user wants said next into one prompt.
|
| 267 |
+
# Exclude load_ref_audio's trailing silence pad (tail_pad_s=0.1) so Whisper
|
| 268 |
+
# doesn't hallucinate tokens over silence.
|
| 269 |
+
speech_samples = max(ref_wav.shape[-1] - int(0.1 * vae_sample_rate), 1)
|
| 270 |
+
resolved_ref_text = ref_text.strip() or transcribe_ref_audio(
|
| 271 |
+
ref_wav[..., :speech_samples], sr=vae_sample_rate,
|
| 272 |
+
)
|
| 273 |
+
combined_text = join_ref_target_text(resolved_ref_text, prompt)
|
| 274 |
+
full_prompt = f"[Speech with voice] {combined_text}"
|
| 275 |
+
|
| 276 |
+
# Build one waveform [ref audio | silence] and encode it as a single clip —
|
| 277 |
+
# the model generates the target portion conditioned on the ref portion.
|
| 278 |
+
total_wav_len = int(duration * vae_sample_rate)
|
| 279 |
+
ref_wav_1d = ref_wav.squeeze(0) if ref_wav.dim() == 2 else ref_wav
|
| 280 |
+
ref_wav_len = min(ref_wav_1d.shape[-1], total_wav_len)
|
| 281 |
+
source_wav = torch.zeros(1, total_wav_len, device=DEVICE)
|
| 282 |
+
source_wav[:, :ref_wav_len] = ref_wav_1d[:ref_wav_len]
|
| 283 |
+
source_latent = audio_vae.wrapped_encode(source_wav) * vae_scale_factor
|
| 284 |
+
|
| 285 |
+
mask_wav = torch.zeros(1, 1, total_wav_len, device=DEVICE)
|
| 286 |
+
mask_wav[:, :, :ref_wav_len] = 2.0 # 2 = reference region, 0 = target (see sample_latents docstring)
|
| 287 |
+
mask_latent = torch.nn.functional.interpolate(
|
| 288 |
+
mask_wav, size=source_latent.shape[-1]
|
| 289 |
+
).to(torch.bfloat16)
|
| 290 |
+
|
| 291 |
+
latents = sample_latents(
|
| 292 |
+
model, scheduler, omni_extractor, [full_prompt],
|
| 293 |
+
source_latents=source_latent, masks=mask_latent,
|
| 294 |
+
num_inference_steps=steps, guidance_scale=guidance, device=DEVICE,
|
| 295 |
+
)
|
| 296 |
+
latents_full = latents * (1.0 / vae_scale_factor)
|
| 297 |
+
|
| 298 |
+
# Decode the full ref+target latent, then crop out just the target —
|
| 299 |
+
# the decoder needs the ref portion as context to decode cleanly.
|
| 300 |
+
ref_samples = int(ref_audio_duration * vae_sample_rate)
|
| 301 |
+
decode_and_save_full(
|
| 302 |
+
audio_vae, latents_full, ref_samples,
|
| 303 |
+
[duration - ref_audio_duration], [out_path], sample_rate=vae_sample_rate,
|
| 304 |
+
)
|
| 305 |
+
|
| 306 |
+
else:
|
| 307 |
+
raise gr.Error(f"Unknown mode: {mode}")
|
|
|
|
| 308 |
|
| 309 |
return out_path
|
| 310 |
|