feat: Drama Studio 프로젝트 초기 구조 설정
- FastAPI 백엔드 (audio-studio-api) - Next.js 프론트엔드 (audio-studio-ui) - Qwen3-TTS 엔진 (audio-studio-tts) - MusicGen 서비스 (audio-studio-musicgen) - Docker Compose 개발/운영 환경 Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
205
audio-studio-musicgen/app/main.py
Normal file
205
audio-studio-musicgen/app/main.py
Normal file
@ -0,0 +1,205 @@
|
||||
"""Audio Studio MusicGen API
|
||||
|
||||
AI 음악 생성 API 서버
|
||||
"""
|
||||
|
||||
import logging
|
||||
from contextlib import asynccontextmanager
|
||||
from typing import Optional
|
||||
|
||||
from fastapi import FastAPI, HTTPException, UploadFile, File, Form
|
||||
from fastapi.responses import Response
|
||||
from pydantic import BaseModel, Field
|
||||
|
||||
from app.services.musicgen_service import musicgen_service
|
||||
|
||||
# 로깅 설정
|
||||
logging.basicConfig(
|
||||
level=logging.INFO,
|
||||
format="%(asctime)s - %(name)s - %(levelname)s - %(message)s"
|
||||
)
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
# ========================================
|
||||
# Pydantic 모델
|
||||
# ========================================
|
||||
|
||||
class GenerateRequest(BaseModel):
|
||||
"""음악 생성 요청"""
|
||||
prompt: str = Field(..., min_length=5, max_length=500, description="음악 설명")
|
||||
duration: int = Field(default=30, ge=5, le=30, description="생성 길이 (초)")
|
||||
top_k: int = Field(default=250, ge=50, le=500, description="top-k 샘플링")
|
||||
temperature: float = Field(default=1.0, ge=0.5, le=2.0, description="생성 다양성")
|
||||
|
||||
|
||||
class HealthResponse(BaseModel):
|
||||
"""헬스체크 응답"""
|
||||
status: str
|
||||
model_info: dict
|
||||
|
||||
|
||||
# ========================================
|
||||
# 앱 생명주기
|
||||
# ========================================
|
||||
|
||||
@asynccontextmanager
|
||||
async def lifespan(app: FastAPI):
|
||||
"""앱 시작/종료 시 실행"""
|
||||
logger.info("MusicGen 서비스 시작...")
|
||||
try:
|
||||
await musicgen_service.initialize()
|
||||
logger.info("MusicGen 서비스 준비 완료")
|
||||
except Exception as e:
|
||||
logger.error(f"MusicGen 초기화 실패: {e}")
|
||||
# 초기화 실패해도 서버는 시작 (lazy loading 시도)
|
||||
|
||||
yield
|
||||
|
||||
logger.info("MusicGen 서비스 종료")
|
||||
|
||||
|
||||
# ========================================
|
||||
# FastAPI 앱
|
||||
# ========================================
|
||||
|
||||
app = FastAPI(
|
||||
title="Audio Studio MusicGen",
|
||||
description="AI 음악 생성 API (Meta AudioCraft)",
|
||||
version="0.1.0",
|
||||
lifespan=lifespan,
|
||||
)
|
||||
|
||||
|
||||
# ========================================
|
||||
# API 엔드포인트
|
||||
# ========================================
|
||||
|
||||
@app.get("/health", response_model=HealthResponse)
|
||||
async def health_check():
|
||||
"""헬스체크 엔드포인트"""
|
||||
return HealthResponse(
|
||||
status="healthy" if musicgen_service.is_initialized() else "initializing",
|
||||
model_info=musicgen_service.get_model_info(),
|
||||
)
|
||||
|
||||
|
||||
@app.post("/generate")
|
||||
async def generate_music(request: GenerateRequest):
|
||||
"""텍스트 프롬프트로 음악 생성
|
||||
|
||||
예시 프롬프트:
|
||||
- "upbeat electronic music for gaming"
|
||||
- "calm piano music, peaceful, ambient"
|
||||
- "energetic rock music with drums"
|
||||
- "lo-fi hip hop beats, relaxing"
|
||||
"""
|
||||
try:
|
||||
audio_bytes = await musicgen_service.generate(
|
||||
prompt=request.prompt,
|
||||
duration=request.duration,
|
||||
top_k=request.top_k,
|
||||
temperature=request.temperature,
|
||||
)
|
||||
|
||||
return Response(
|
||||
content=audio_bytes,
|
||||
media_type="audio/wav",
|
||||
headers={
|
||||
"X-Sample-Rate": "32000",
|
||||
"X-Duration": str(request.duration),
|
||||
"Content-Disposition": 'attachment; filename="generated_music.wav"',
|
||||
},
|
||||
)
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"음악 생성 실패: {e}")
|
||||
raise HTTPException(status_code=500, detail=f"Music generation failed: {str(e)}")
|
||||
|
||||
|
||||
@app.post("/generate-with-melody")
|
||||
async def generate_with_melody(
|
||||
prompt: str = Form(..., min_length=5, description="음악 설명"),
|
||||
duration: int = Form(default=30, ge=5, le=30, description="생성 길이"),
|
||||
melody_audio: UploadFile = File(..., description="참조 멜로디 오디오"),
|
||||
):
|
||||
"""멜로디 조건부 음악 생성
|
||||
|
||||
참조 멜로디의 멜로디/하모니를 유지하면서 새로운 음악 생성
|
||||
"""
|
||||
try:
|
||||
melody_bytes = await melody_audio.read()
|
||||
|
||||
if len(melody_bytes) < 1000:
|
||||
raise HTTPException(status_code=400, detail="Melody audio is too small")
|
||||
|
||||
audio_bytes = await musicgen_service.generate_with_melody(
|
||||
prompt=prompt,
|
||||
melody_audio=melody_bytes,
|
||||
duration=duration,
|
||||
)
|
||||
|
||||
return Response(
|
||||
content=audio_bytes,
|
||||
media_type="audio/wav",
|
||||
headers={
|
||||
"X-Sample-Rate": "32000",
|
||||
"X-Duration": str(duration),
|
||||
"Content-Disposition": 'attachment; filename="melody_based_music.wav"',
|
||||
},
|
||||
)
|
||||
|
||||
except HTTPException:
|
||||
raise
|
||||
except Exception as e:
|
||||
logger.error(f"멜로디 기반 생성 실패: {e}")
|
||||
raise HTTPException(status_code=500, detail=f"Generation failed: {str(e)}")
|
||||
|
||||
|
||||
@app.get("/prompts")
|
||||
async def get_example_prompts():
|
||||
"""예시 프롬프트 목록"""
|
||||
return {
|
||||
"examples": [
|
||||
{
|
||||
"category": "Electronic",
|
||||
"prompts": [
|
||||
"upbeat electronic dance music with synthesizers",
|
||||
"chill electronic ambient music",
|
||||
"retro synthwave 80s style music",
|
||||
],
|
||||
},
|
||||
{
|
||||
"category": "Classical",
|
||||
"prompts": [
|
||||
"calm piano solo, classical style",
|
||||
"orchestral epic cinematic music",
|
||||
"gentle string quartet, romantic",
|
||||
],
|
||||
},
|
||||
{
|
||||
"category": "Pop/Rock",
|
||||
"prompts": [
|
||||
"energetic rock music with electric guitar",
|
||||
"upbeat pop song with catchy melody",
|
||||
"acoustic guitar folk music",
|
||||
],
|
||||
},
|
||||
{
|
||||
"category": "Ambient/Lo-fi",
|
||||
"prompts": [
|
||||
"lo-fi hip hop beats, relaxing, study music",
|
||||
"peaceful ambient nature sounds music",
|
||||
"meditation music, calm, zen",
|
||||
],
|
||||
},
|
||||
{
|
||||
"category": "Game/Film",
|
||||
"prompts": [
|
||||
"epic adventure game soundtrack",
|
||||
"tense suspenseful thriller music",
|
||||
"cheerful happy video game background",
|
||||
],
|
||||
},
|
||||
]
|
||||
}
|
||||
Reference in New Issue
Block a user