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:
2026-07-26 17:51:37 +07:00
parent 44e0a6d736
commit e51b7fd355
8 changed files with 426 additions and 26 deletions
+11 -1
View File
@@ -1,4 +1,4 @@
import os
import os, threading
from fastapi import FastAPI
from fastapi.responses import HTMLResponse
from fastapi.staticfiles import StaticFiles
@@ -14,6 +14,7 @@ from app.api.v1.user_config import router as user_config_router
from app.api.v1.ai_proxy import router as ai_proxy_router
from app.api.v1.plugins import router as plugins_router
from app.core.auth import seed_admin
from app.core.soundfont_converter import SoundFontConverter
# Ensure storage directories exist
os.makedirs(settings.UPLOADS_DIR, exist_ok=True)
@@ -55,6 +56,15 @@ app.include_router(plugins_router, prefix="/api/v1/plugins", tags=["plugins"])
async def startup_seed_admin():
seed_admin()
@app.on_event("startup")
async def startup_convert_soundfonts():
def _run():
try:
SoundFontConverter().batch_convert_all()
except Exception as e:
print(f"[Startup] SoundFont conversion error: {e}")
threading.Thread(target=_run, daemon=True).start()
@app.get("/", response_class=HTMLResponse)
async def get_index():
index_path = os.path.join(settings.TEMPLATES_DIR, "index.html")