From ebf0828c55c334c6fabc8a361c9693d995807cbc Mon Sep 17 00:00:00 2001 From: locphamtran Date: Tue, 25 Aug 2026 09:45:08 +0700 Subject: [PATCH] =?UTF-8?q?fix(render):=20offline=20SF=20fallback=20t?= =?UTF-8?q?=C3=ACm=20font=20GM=20c=C3=B3=20s=E1=BA=B5n=20trong=20plugin=5F?= =?UTF-8?q?dirs=20user=20(Windows)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/core/render_engine.py | 36 ++++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/app/core/render_engine.py b/app/core/render_engine.py index f9f998f..eb01476 100644 --- a/app/core/render_engine.py +++ b/app/core/render_engine.py @@ -21,6 +21,13 @@ SYS_SOUNDFONTS = [ ("SGM-V2.01.sf2", "SGM v2.01"), ] +# GM font dự phòng khi track dùng sf_generaluser_gs nhưng máy không có font đó +# (Windows không ship SF2; SYSTEM_SF_DIR là path Linux). Mirror policy frontend +# realtime (app.jsx pickFallbackSoundfont: sgm → generaluser → tgsf21x → timgm +# → font đầu tiên) — offline render phải ra tiếng GM gần đúng, không rơi xuống +# oscillator synth. +GM_SF_FALLBACK_STEMS = ["sgm", "generaluser", "tgsf21x", "timgm", "fluid", "chorium", "musescore"] + def _find_sf2_path(sf_id: str) -> str: clean_id = sf_id.replace("sf_", "") if sf_id.startswith("sf_") else sf_id clean_lower = clean_id.lower() @@ -61,6 +68,35 @@ def _find_default_sf2() -> str: p = os.path.join(base_dir, sf_name) if os.path.exists(p): return p + # Windows: scan plugin dirs user — ưu tiên font GM quen thuộc (sgm/ + # generaluser/tgsf21x...), bất kỳ SF2 khác làm fallback cuối (vẫn hơn + # oscillator). Walk sâu: soundfont có thể nằm trong subdir (vd + # Downloads\Collections soundfont\\foo.sf2). + try: + from app.core.vst_engine import _load_user_plugin_dirs + found = [] + for base_dir in _load_user_plugin_dirs(): + if not os.path.isdir(base_dir): + continue + for root, dirs, files in os.walk(base_dir): + for fname in files: + _fb, fext = os.path.splitext(fname) + if fext.lower() not in (".sf2", ".sf3"): + continue + fname_l = fname.lower() + score = 0 + for i, stem in enumerate(GM_SF_FALLBACK_STEMS): + if stem in fname_l: + score = 100 - i + break + if score == 0 and ("gm" in fname_l or "general" in fname_l): + score = 50 + found.append((score, os.path.join(root, fname))) + if found: + found.sort(key=lambda x: -x[0]) + return found[0][1] + except Exception: + pass return "" def _apply_master_volume_automation(buf, sr, nodes):