File size: 4,309 Bytes
0be8d78 f204472 8e525cc 0be8d78 d327b90 0be8d78 d327b90 0be8d78 8e525cc ba3eef7 0be8d78 8e525cc dd0d30c 0be8d78 e4dce8d ba3eef7 d2068ea 8442b37 2d15ecf ba3eef7 31b39bd 0be8d78 31b39bd 0be8d78 ba3eef7 0be8d78 31c7598 0be8d78 ba3eef7 0be8d78 8e525cc 0be8d78 5fae22d 0be8d78 5fae22d 0be8d78 8e525cc 0be8d78 8e525cc 0be8d78 8e525cc 31c7598 0be8d78 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 | import os
import uuid
import threading
import queue
from typing import Dict, Optional
import torch
import soundfile as sf
from fastapi import FastAPI, BackgroundTasks, HTTPException
from fastapi.responses import FileResponse
from diffusers import DiffusionPipeline
from huggingface_hub import login
# -------------------- App --------------------
app = FastAPI()
OUTPUT_DIR = "outputs"
os.makedirs(OUTPUT_DIR, exist_ok=True)
DEVICE = "cuda" if torch.cuda.is_available() else "cpu"
DTYPE = torch.float16 if DEVICE == "cuda" else torch.float32
pipe = None # lazy-loaded
# -------------------- HF Login --------------------
login(token=os.environ["HF_TOKEN"])
# -------------------- Pipeline --------------------
def load_pipeline():
global pipe
if pipe is None:
print("Loading Stable Audio Open pipeline...")
pipe = DiffusionPipeline.from_pretrained(
"stabilityai/stable-audio-open-1.0",
torch_dtype=DTYPE,
trust_remote_code=True,
revision="main",
low_cpu_mem_usage=False,
)
pipe.enable_attention_slicing()
pipe.to(DEVICE)
print("Pipeline loaded on", DEVICE)
@app.on_event("startup")
def startup():
load_pipeline()
start_worker()
# -------------------- Job State --------------------
class Job:
def __init__(self, prompt: str, duration: float):
self.id = uuid.uuid4().hex
self.prompt = prompt
self.duration = duration
self.progress = 0
self.status = "queued" # queued | running | done | error
self.filepath: Optional[str] = None
self.error: Optional[str] = None
jobs: Dict[str, Job] = {}
job_queue: queue.Queue[Job] = queue.Queue()
# -------------------- Worker Thread --------------------
def worker_loop():
while True:
job: Job = job_queue.get()
try:
job.status = "running"
job.progress = 5
load_pipeline()
pipe.audio_length_in_s = float(job.duration)
with torch.no_grad():
output = pipe(
prompt=job.prompt,
guidance_scale=7.5,
num_inference_steps=150,
)
job.progress = 90
audio = output.audios[0]
if isinstance(audio, torch.Tensor):
audio = audio.detach().cpu().numpy()
if audio.ndim == 2 and audio.shape[0] < audio.shape[1]:
audio = audio.T
audio = audio.astype("float32").clip(-1.0, 1.0)
filename = f"{job.id}.wav"
path = os.path.join(OUTPUT_DIR, filename)
sf.write(path, audio, samplerate=44100)
job.filepath = path
job.progress = 100
job.status = "done"
except Exception as e:
job.status = "error"
job.error = str(e)
finally:
job_queue.task_done()
def start_worker():
t = threading.Thread(target=worker_loop, daemon=True)
t.start()
# -------------------- API --------------------
@app.post("/generate")
def generate(prompt: str, duration: float = 10.0):
job = Job(prompt=prompt, duration=duration)
jobs[job.id] = job
job_queue.put(job)
return {
"job_id": job.id,
"status": job.status,
}
@app.get("/status/{job_id}")
def status(job_id: str):
job = jobs.get(job_id)
if not job:
raise HTTPException(status_code=404, detail="Job not found")
return {
"job_id": job.id,
"status": job.status,
"progress": job.progress,
"error": job.error,
"ready": job.status == "done",
}
def cleanup_file(path: str):
try:
os.remove(path)
except Exception:
pass
@app.get("/download/{job_id}")
def download(job_id: str, background_tasks: BackgroundTasks):
job = jobs.get(job_id)
if not job:
raise HTTPException(status_code=404, detail="Job not found")
if job.status != "done" or not job.filepath:
raise HTTPException(status_code=400, detail="File not ready")
background_tasks.add_task(cleanup_file, job.filepath)
return FileResponse(
path=job.filepath,
media_type="audio/wav",
filename=os.path.basename(job.filepath),
)
|