feat: SF3 conversion + SpessaSynth client player
Server:
- soundfont_converter.py: Python SF2->SF3 via ffmpeg Ogg compression
- batch_convert_all runs on startup (daemon thread)
- GET /soundfonts/download/{sf_id} serves SF3 with SF2 fallback
- Dockerfile: add fluidsynth, vorbis-tools
Client:
- soundfontStorage.js: IndexedDB cache for SF3 buffers
- soundfontPlayer.js: dual-mode (SpessaSynth + oscillator fallback)
- app.jsx: init SpessaSynth, loadSF on instrument select
- index.html: SpessaSynth CDN import + storage script tag
Compression: DSK 11M->1.1M (90%), SGM 529M->18M (97%)
This commit is contained in:
@@ -1,11 +1,13 @@
|
||||
import os, uuid, json, tempfile
|
||||
from fastapi import APIRouter, HTTPException, Depends, UploadFile, File, BackgroundTasks
|
||||
from fastapi.responses import FileResponse
|
||||
from pydantic import BaseModel
|
||||
from typing import Optional, Any
|
||||
from app.config import settings
|
||||
from app.core.vst_engine import PluginManager, HAS_PEDALBOARD, HAS_PYFLUIDSYNTH
|
||||
from app.core.render_engine import PythonRenderEngine
|
||||
from app.core.soundfont_inspector import SoundFontInspector
|
||||
from app.core.soundfont_converter import SoundFontConverter
|
||||
from app.api.v1.auth import get_current_user
|
||||
|
||||
router = APIRouter()
|
||||
@@ -123,6 +125,27 @@ async def delete_soundfont(sf_id: str, current_user: dict = Depends(get_current_
|
||||
return {"deleted": True, "sf_id": sf_id}
|
||||
|
||||
|
||||
@router.get("/soundfonts/download/{sf_id}")
|
||||
async def download_soundfont_asset(sf_id: str):
|
||||
clean_id = sf_id.replace("sf_", "") if sf_id.startswith("sf_") else sf_id
|
||||
for base_dir in [UPLOAD_SF_DIR, SYSTEM_SF_DIR]:
|
||||
if not os.path.isdir(base_dir):
|
||||
continue
|
||||
for ext in [".sf3", ".sf2"]:
|
||||
for fname in os.listdir(base_dir):
|
||||
fbase, fext = os.path.splitext(fname)
|
||||
if fext.lower() == ext and fbase.lower() == clean_id.lower():
|
||||
full = os.path.join(base_dir, fname)
|
||||
return FileResponse(full, media_type="application/octet-stream", filename=f"{fbase}{ext}")
|
||||
# Fallback: try exact match on sf_id
|
||||
for base_dir in [UPLOAD_SF_DIR, SYSTEM_SF_DIR]:
|
||||
for ext in [".sf3", ".sf2"]:
|
||||
full = os.path.join(base_dir, clean_id + ext)
|
||||
if os.path.exists(full):
|
||||
return FileResponse(full, media_type="application/octet-stream", filename=f"{clean_id}{ext}")
|
||||
raise HTTPException(status_code=404, detail="SoundFont asset not found")
|
||||
|
||||
|
||||
class RenderRequest(BaseModel):
|
||||
project_json: dict
|
||||
output_filename: Optional[str] = "render_output.wav"
|
||||
|
||||
Reference in New Issue
Block a user