Spaces:
Sleeping
Sleeping
| import torch | |
| import scipy | |
| import tempfile | |
| import os | |
| import uuid | |
| from pathlib import Path | |
| from fastapi import FastAPI, Request | |
| from fastapi.responses import HTMLResponse, FileResponse, JSONResponse | |
| import uvicorn | |
| from diffusers import AudioLDMPipeline | |
| print("Loading AudioLDM Small model...") | |
| pipe = AudioLDMPipeline.from_pretrained( | |
| "cvssp/audioldm-s-full-v2", | |
| torch_dtype=torch.float32, | |
| ) | |
| print("Model loaded successfully!") | |
| app = FastAPI() | |
| OUTPUT_DIR = Path(tempfile.gettempdir()) / "sfx_outputs" | |
| OUTPUT_DIR.mkdir(exist_ok=True) | |
| HTML_PAGE = """<!DOCTYPE html> | |
| <html lang="en"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
| <title>🔊 AudioLDM - Sound Effects Generator</title> | |
| <style> | |
| * { box-sizing: border-box; margin: 0; padding: 0; } | |
| body { font-family: 'Segoe UI', sans-serif; background: #0b0f19; color: #e5e7eb; min-height: 100vh; display: flex; justify-content: center; padding: 2rem; } | |
| .container { max-width: 600px; width: 100%; } | |
| h1 { text-align: center; font-size: 1.8rem; margin-bottom: 0.5rem; } | |
| .subtitle { text-align: center; color: #9ca3af; margin-bottom: 2rem; font-size: 0.9rem; } | |
| .field { margin-bottom: 1.2rem; } | |
| label { display: block; font-weight: 600; margin-bottom: 0.4rem; font-size: 0.9rem; } | |
| input[type=text], textarea { width: 100%; padding: 0.7rem; border-radius: 8px; border: 1px solid #374151; background: #1f2937; color: #e5e7eb; font-size: 0.95rem; } | |
| input[type=range] { width: 100%; accent-color: #6366f1; } | |
| .range-info { display: flex; justify-content: space-between; font-size: 0.8rem; color: #9ca3af; } | |
| button { width: 100%; padding: 0.9rem; background: #6366f1; color: white; border: none; border-radius: 8px; font-size: 1rem; font-weight: 600; cursor: pointer; margin-top: 0.5rem; } | |
| button:hover { background: #4f46e5; } | |
| button:disabled { background: #374151; cursor: not-allowed; } | |
| .status { text-align: center; margin-top: 1rem; padding: 0.8rem; border-radius: 8px; display: none; } | |
| .status.loading { display: block; background: #1e3a5f; color: #93c5fd; } | |
| .status.error { display: block; background: #3b1111; color: #fca5a5; } | |
| .status.success { display: block; background: #0f2e1a; color: #86efac; } | |
| .result { margin-top: 1.5rem; display: none; } | |
| .result.show { display: block; } | |
| audio { width: 100%; margin-top: 0.5rem; } | |
| .download { display: inline-block; margin-top: 0.5rem; color: #818cf8; text-decoration: none; } | |
| .examples { margin-top: 2rem; } | |
| .examples h3 { font-size: 0.9rem; margin-bottom: 0.5rem; color: #9ca3af; } | |
| .example-btn { display: inline-block; padding: 0.4rem 0.8rem; background: #1f2937; border: 1px solid #374151; border-radius: 6px; margin: 0.2rem; cursor: pointer; font-size: 0.8rem; color: #d1d5db; } | |
| .example-btn:hover { border-color: #6366f1; color: #a5b4fc; } | |
| </style> | |
| </head> | |
| <body> | |
| <div class="container"> | |
| <h1>🔊 AudioLDM SFX Generator</h1> | |
| <p class="subtitle">Generate sound effects from text using AudioLDM (fast model)</p> | |
| <div class="field"> | |
| <label for="prompt">Prompt</label> | |
| <input type="text" id="prompt" placeholder="cinematic whoosh, explosion, footsteps on gravel..."> | |
| </div> | |
| <div class="field"> | |
| <label for="neg">Negative Prompt</label> | |
| <input type="text" id="neg" value="Low quality, distorted, noise, music."> | |
| </div> | |
| <div class="field"> | |
| <label>Duration: <span id="dur-val">3</span>s</label> | |
| <input type="range" id="duration" min="1" max="10" value="3" step="1" oninput="document.getElementById('dur-val').textContent=this.value"> | |
| <div class="range-info"><span>1s</span><span>10s</span></div> | |
| </div> | |
| <div class="field"> | |
| <label>Steps: <span id="steps-val">20</span></label> | |
| <input type="range" id="steps" min="10" max="50" value="20" step="5" oninput="document.getElementById('steps-val').textContent=this.value"> | |
| <div class="range-info"><span>10 (fast)</span><span>50 (quality)</span></div> | |
| </div> | |
| <button id="gen-btn" onclick="generate()">🎵 Generate Sound Effect</button> | |
| <div id="status" class="status"></div> | |
| <div id="result" class="result"> | |
| <audio id="audio" controls></audio><br> | |
| <a id="dl-link" class="download" href="#" download="sfx.wav">⬇ Download WAV</a> | |
| </div> | |
| <div class="examples"> | |
| <h3>Examples (click to fill):</h3> | |
| <span class="example-btn" onclick="fillExample('cinematic whoosh transition sound')">cinematic whoosh</span> | |
| <span class="example-btn" onclick="fillExample('deep bass impact hit')">bass impact</span> | |
| <span class="example-btn" onclick="fillExample('footsteps walking on gravel')">footsteps gravel</span> | |
| <span class="example-btn" onclick="fillExample('thunder and rain storm')">thunder rain</span> | |
| <span class="example-btn" onclick="fillExample('digital glitch notification beep')">glitch beep</span> | |
| <span class="example-btn" onclick="fillExample('sword swing slash whoosh')">sword slash</span> | |
| <span class="example-btn" onclick="fillExample('car engine revving')">car engine</span> | |
| <span class="example-btn" onclick="fillExample('ocean waves crashing on shore')">ocean waves</span> | |
| </div> | |
| </div> | |
| <script> | |
| function fillExample(t) { document.getElementById('prompt').value = t; } | |
| async function generate() { | |
| const btn = document.getElementById('gen-btn'); | |
| const status = document.getElementById('status'); | |
| const result = document.getElementById('result'); | |
| const prompt = document.getElementById('prompt').value.trim(); | |
| if (!prompt) { status.className='status error'; status.textContent='Please enter a prompt.'; return; } | |
| btn.disabled = true; | |
| btn.textContent = '⏳ Generating...'; | |
| status.className = 'status loading'; | |
| status.textContent = 'Generating sound effect... ~5-15 seconds.'; | |
| result.className = 'result'; | |
| try { | |
| const res = await fetch('/api/generate', { | |
| method: 'POST', | |
| headers: {'Content-Type': 'application/json'}, | |
| body: JSON.stringify({ | |
| prompt: prompt, | |
| negative_prompt: document.getElementById('neg').value, | |
| duration: parseInt(document.getElementById('duration').value), | |
| steps: parseInt(document.getElementById('steps').value) | |
| }) | |
| }); | |
| if (!res.ok) { | |
| const err = await res.json(); | |
| throw new Error(err.detail || 'Generation failed'); | |
| } | |
| const data = await res.json(); | |
| document.getElementById('audio').src = data.url; | |
| document.getElementById('dl-link').href = data.url; | |
| result.className = 'result show'; | |
| status.className = 'status success'; | |
| status.textContent = 'Done! Generation time: ' + data.generation_time.toFixed(1) + 's'; | |
| } catch(e) { | |
| status.className = 'status error'; | |
| status.textContent = 'Error: ' + e.message; | |
| } finally { | |
| btn.disabled = false; | |
| btn.textContent = '🎵 Generate Sound Effect'; | |
| } | |
| } | |
| </script> | |
| </body> | |
| </html>""" | |
| async def index(): | |
| return HTML_PAGE | |
| async def generate(request: Request): | |
| import time | |
| body = await request.json() | |
| prompt = body.get("prompt", "").strip() | |
| if not prompt: | |
| return JSONResponse({"detail": "Prompt is required"}, status_code=400) | |
| negative_prompt = body.get("negative_prompt", "Low quality, distorted.") | |
| duration = min(max(int(body.get("duration", 3)), 1), 10) | |
| steps = min(max(int(body.get("steps", 20)), 10), 50) | |
| start = time.time() | |
| audio = pipe( | |
| prompt=prompt, | |
| negative_prompt=negative_prompt, | |
| num_inference_steps=steps, | |
| audio_length_in_s=float(duration), | |
| guidance_scale=2.5, | |
| num_waveforms_per_prompt=1, | |
| ).audios[0] | |
| gen_time = time.time() - start | |
| filename = f"sfx_{uuid.uuid4().hex[:8]}.wav" | |
| out_path = OUTPUT_DIR / filename | |
| scipy.io.wavfile.write(str(out_path), rate=16000, data=audio) | |
| return {"url": f"/files/{filename}", "generation_time": gen_time} | |
| async def get_file(filename: str): | |
| filepath = OUTPUT_DIR / filename | |
| if not filepath.exists(): | |
| return JSONResponse({"detail": "File not found"}, status_code=404) | |
| return FileResponse(str(filepath), media_type="audio/wav", filename=filename) | |
| if __name__ == "__main__": | |
| uvicorn.run(app, host="0.0.0.0", port=7860) | |