Spaces:
Running
on
A10G
Running
on
A10G
| """ | |
| MagicPath AI Vocal Effects Server - DiffVox LLM ํตํฉ ๋ฒ์ | |
| ========================================================= | |
| Dry ๋ณด์ปฌ ํ์ผ์ ๋ฐ์์ ํ์ต๋ AI๊ฐ ์ดํํฐ ํ๋ผ๋ฏธํฐ๋ฅผ ์์ธกํ๊ณ , | |
| ์ค์ ๋ก ์ดํํธ๋ฅผ ์ ์ฉํ ์ค๋์ค๋ฅผ ๋ฐํํ๋ ์๋ฒ | |
| """ | |
| from fastapi import FastAPI, UploadFile, File, Form, HTTPException | |
| from fastapi.middleware.cors import CORSMiddleware | |
| from fastapi.responses import FileResponse, JSONResponse | |
| import tempfile | |
| import os | |
| import uuid | |
| from pathlib import Path | |
| # ๋ด๋ถ ๋ชจ๋ | |
| from models.ai_effector import AIEffector | |
| from audio_processing.effect_chain import EffectChain | |
| # ============================================ | |
| # ์ค์ | |
| # ============================================ | |
| # ํ์ต๋ ๋ชจ๋ธ ๊ฒฝ๋ก (Hugging Face ๋ ํฌ ๋๋ ๋ก์ปฌ ๊ฒฝ๋ก) | |
| MODEL_PATH = os.environ.get("DIFFVOX_MODEL_PATH", "heybaeheef/KU_SW_Academy") | |
| BASE_MODEL_NAME = os.environ.get("BASE_MODEL_NAME", "Qwen/Qwen3-8B") | |
| AUDIO_FEATURE_DIM = int(os.environ.get("AUDIO_FEATURE_DIM", "64")) | |
| USE_HUGGINGFACE = os.environ.get("USE_HUGGINGFACE", "true").lower() == "true" | |
| # ============================================ | |
| # FastAPI ์ฑ ์ด๊ธฐํ | |
| # ============================================ | |
| app = FastAPI( | |
| title="MagicPath AI Vocal Effects", | |
| description="AI-powered vocal effect processing server (DiffVox LLM ํตํฉ)", | |
| version="2.0.0" | |
| ) | |
| # CORS ์ค์ | |
| app.add_middleware( | |
| CORSMiddleware, | |
| allow_origins=["*"], # ๋ฐฐํฌ ์ ํน์ ๋๋ฉ์ธ์ผ๋ก ์ ํ ๊ถ์ฅ | |
| allow_credentials=True, | |
| allow_methods=["*"], | |
| allow_headers=["*"], | |
| ) | |
| # ์ ์ญ ๊ฐ์ฒด ์ด๊ธฐํ | |
| print("=" * 60) | |
| print("MagicPath AI Vocal Effects Server v2.0") | |
| print("=" * 60) | |
| print(f"Model Path: {MODEL_PATH}") | |
| print(f"Base Model: {BASE_MODEL_NAME}") | |
| print(f"Audio Feature Dim: {AUDIO_FEATURE_DIM}") | |
| print(f"Use Hugging Face: {USE_HUGGINGFACE}") | |
| print("=" * 60) | |
| ai_effector = AIEffector( | |
| model_path=MODEL_PATH, | |
| base_model_name=BASE_MODEL_NAME, | |
| audio_feature_dim=AUDIO_FEATURE_DIM, | |
| use_huggingface=USE_HUGGINGFACE | |
| ) | |
| effect_chain = EffectChain() | |
| # ์์ ํ์ผ ์ ์ฅ ๊ฒฝ๋ก | |
| TEMP_DIR = Path(tempfile.gettempdir()) / "magicpath" | |
| TEMP_DIR.mkdir(exist_ok=True) | |
| # ============================================ | |
| # API ์๋ํฌ์ธํธ | |
| # ============================================ | |
| async def root(): | |
| """์๋ฒ ์ ๋ณด""" | |
| return { | |
| "status": "running", | |
| "message": "MagicPath AI Vocal Effects Server v2.0 (DiffVox LLM)", | |
| "ai_model_loaded": ai_effector.is_loaded(), | |
| "endpoints": { | |
| "POST /process": "์ค๋์ค ํ์ผ ์ฒ๋ฆฌ ํ ๋ฐํ", | |
| "POST /predict": "ํ๋ผ๋ฏธํฐ๋ง ์์ธก (JSON)", | |
| "GET /health": "์๋ฒ ์ํ ํ์ธ" | |
| } | |
| } | |
| async def health_check(): | |
| """์๋ฒ ๋ฐ ๋ชจ๋ธ ์ํ ํ์ธ""" | |
| return { | |
| "status": "healthy", | |
| "ai_model_loaded": ai_effector.is_loaded(), | |
| "supported_effects": effect_chain.get_available_effects(), | |
| "model_path": MODEL_PATH, | |
| "base_model": BASE_MODEL_NAME | |
| } | |
| async def predict_parameters( | |
| audio: UploadFile = File(..., description="Dry ๋ณด์ปฌ ์ค๋์ค ํ์ผ"), | |
| prompt: str = Form("", description="ํ ์คํธ ๋ช ๋ น (์: 'warm', 'bright')") | |
| ): | |
| """ | |
| AI ๋ชจ๋ธ๋ก ์ดํํฐ ํ๋ผ๋ฏธํฐ ์์ธก (์ค๋์ค ์ฒ๋ฆฌ ์์ด) | |
| - audio: wav, mp3 ๋ฑ ์ค๋์ค ํ์ผ | |
| - prompt: ์ํ๋ ์ฌ์ด๋ ์ค๋ช | |
| Returns: ์์ธก๋ ์ดํํฐ ํ๋ผ๋ฏธํฐ JSON | |
| """ | |
| try: | |
| # ์์ ํ์ผ๋ก ์ ์ฅ | |
| input_path = TEMP_DIR / f"{uuid.uuid4()}_{audio.filename}" | |
| with open(input_path, "wb") as f: | |
| content = await audio.read() | |
| f.write(content) | |
| # AI ๋ชจ๋ธ๋ก ํ๋ผ๋ฏธํฐ ์์ธก | |
| parameters = ai_effector.predict( | |
| audio_path=str(input_path), | |
| text_prompt=prompt | |
| ) | |
| # ์์ ํ์ผ ์ญ์ | |
| os.remove(input_path) | |
| return JSONResponse(content={ | |
| "status": "success", | |
| "prompt": prompt, | |
| "ai_model_used": ai_effector.is_loaded(), | |
| "parameters": parameters | |
| }) | |
| except Exception as e: | |
| raise HTTPException(status_code=500, detail=str(e)) | |
| async def process_audio( | |
| audio: UploadFile = File(..., description="Dry ๋ณด์ปฌ ์ค๋์ค ํ์ผ"), | |
| prompt: str = Form("", description="ํ ์คํธ ๋ช ๋ น (์: 'warm', 'bright')") | |
| ): | |
| """ | |
| AI๊ฐ ์์ธกํ ํ๋ผ๋ฏธํฐ๋ก ์ค์ ์ค๋์ค ์ฒ๋ฆฌ | |
| - audio: wav, mp3 ๋ฑ ์ค๋์ค ํ์ผ | |
| - prompt: ์ํ๋ ์ฌ์ด๋ ์ค๋ช | |
| Returns: ์ฒ๋ฆฌ๋ ์ค๋์ค ํ์ผ (wav) | |
| """ | |
| input_path = None | |
| output_path = None | |
| try: | |
| # ์์ ํ์ผ ๊ฒฝ๋ก ์์ฑ | |
| file_id = str(uuid.uuid4()) | |
| input_path = TEMP_DIR / f"{file_id}_input_{audio.filename}" | |
| output_path = TEMP_DIR / f"{file_id}_output.wav" | |
| # ์ ๋ ฅ ํ์ผ ์ ์ฅ | |
| with open(input_path, "wb") as f: | |
| content = await audio.read() | |
| f.write(content) | |
| print(f"[Process] ์ ๋ ฅ ํ์ผ: {input_path}") | |
| print(f"[Process] ํ๋กฌํํธ: {prompt}") | |
| # Step 1: AI ๋ชจ๋ธ๋ก ํ๋ผ๋ฏธํฐ ์์ธก | |
| parameters = ai_effector.predict( | |
| audio_path=str(input_path), | |
| text_prompt=prompt | |
| ) | |
| print(f"[Process] ์์ธก๋ ํ๋ผ๋ฏธํฐ: {len(parameters)}๊ฐ") | |
| # Step 2: ์ดํํฐ ์ฒด์ธ์ผ๋ก ์ค๋์ค ์ฒ๋ฆฌ | |
| effect_chain.process( | |
| input_path=str(input_path), | |
| output_path=str(output_path), | |
| parameters=parameters | |
| ) | |
| # ์ ๋ ฅ ํ์ผ ์ญ์ | |
| os.remove(input_path) | |
| # ์ฒ๋ฆฌ๋ ์ค๋์ค ๋ฐํ | |
| return FileResponse( | |
| path=str(output_path), | |
| media_type="audio/wav", | |
| filename=f"processed_{audio.filename.rsplit('.', 1)[0]}.wav", | |
| background=None | |
| ) | |
| except Exception as e: | |
| # ์๋ฌ ์ ์์ ํ์ผ ์ ๋ฆฌ | |
| if input_path and input_path.exists(): | |
| os.remove(input_path) | |
| if output_path and output_path.exists(): | |
| os.remove(output_path) | |
| print(f"[Process] โ ์๋ฌ: {e}") | |
| import traceback | |
| traceback.print_exc() | |
| raise HTTPException(status_code=500, detail=str(e)) | |
| async def process_audio_with_params( | |
| audio: UploadFile = File(..., description="Dry ๋ณด์ปฌ ์ค๋์ค ํ์ผ"), | |
| prompt: str = Form("", description="ํ ์คํธ ๋ช ๋ น") | |
| ): | |
| """ | |
| ์ค๋์ค ์ฒ๋ฆฌ + ์ฌ์ฉ๋ ํ๋ผ๋ฏธํฐ๋ ํจ๊ป ๋ฐํ | |
| Returns: JSON (์ฒ๋ฆฌ๋ ์ค๋์ค URL + ํ๋ผ๋ฏธํฐ) | |
| """ | |
| input_path = None | |
| output_path = None | |
| try: | |
| file_id = str(uuid.uuid4()) | |
| input_path = TEMP_DIR / f"{file_id}_input_{audio.filename}" | |
| output_path = TEMP_DIR / f"{file_id}_output.wav" | |
| with open(input_path, "wb") as f: | |
| content = await audio.read() | |
| f.write(content) | |
| # AI ํ๋ผ๋ฏธํฐ ์์ธก | |
| parameters = ai_effector.predict( | |
| audio_path=str(input_path), | |
| text_prompt=prompt | |
| ) | |
| # ์ค๋์ค ์ฒ๋ฆฌ | |
| effect_chain.process( | |
| input_path=str(input_path), | |
| output_path=str(output_path), | |
| parameters=parameters | |
| ) | |
| os.remove(input_path) | |
| # Base64 ์ธ์ฝ๋ฉ์ผ๋ก ์ค๋์ค ๋ฐํ (๋๋ URL) | |
| import base64 | |
| with open(output_path, "rb") as f: | |
| audio_base64 = base64.b64encode(f.read()).decode('utf-8') | |
| os.remove(output_path) | |
| return JSONResponse(content={ | |
| "status": "success", | |
| "prompt": prompt, | |
| "ai_model_used": ai_effector.is_loaded(), | |
| "parameters": parameters, | |
| "audio_base64": audio_base64, | |
| "audio_format": "wav" | |
| }) | |
| except Exception as e: | |
| if input_path and input_path.exists(): | |
| os.remove(input_path) | |
| if output_path and output_path.exists(): | |
| os.remove(output_path) | |
| raise HTTPException(status_code=500, detail=str(e)) | |
| if __name__ == "__main__": | |
| import uvicorn | |
| uvicorn.run(app, host="0.0.0.0", port=8000) | |