Gap 12: soundfont render qua native bridge — render_soundfont_offline (FluidSynth C API) thay pyfluidsynth khi thiếu lib; render_engine soundfont branch fallback bridge (test_64, 176 passed)

This commit is contained in:
2026-08-23 08:52:57 +07:00
parent 6b2c9a039b
commit 2b5f396133
3 changed files with 186 additions and 58 deletions
+37 -7
View File
@@ -603,11 +603,11 @@ class PythonRenderEngine:
)
elif soundfont_id or (instrument_id and instrument_id.startswith("sf_")):
sf_path = _find_sf2_path(soundfont_id or instrument_id)
# 3-level fallback: selected SF → default SF → oscillator synth
if not sf_path or not os.path.exists(sf_path) or not HAS_PYFLUIDSYNTH:
if not sf_path or not os.path.exists(sf_path):
logger.warning(f"[RenderEngine] SoundFont not found for {soundfont_id or instrument_id}, trying default")
sf_path = _find_default_sf2() if HAS_PYFLUIDSYNTH else ""
# 4-level fallback: selected SF → default SF → native bridge → oscillator synth.
# Gap 12: pyfluidsynth thiếu FluidSynth lib → bridge render SF2 thật thay vì oscillator.
if not sf_path or not os.path.exists(sf_path):
logger.warning(f"[RenderEngine] SoundFont not found for {soundfont_id or instrument_id}, trying default")
sf_path = _find_default_sf2()
if sf_path and os.path.exists(sf_path) and HAS_PYFLUIDSYNTH:
import fluidsynth as _fs
@@ -651,9 +651,39 @@ class PythonRenderEngine:
_cursor = end_s
synth_buffer = midi_data
_fs.delete_fluid_synth(_fl)
elif sf_path and os.path.exists(sf_path) and settings.RENDER_ENGINE == "bridge":
# Gap 12: native bridge nhúng FluidSynth C API — render SF2/SF3 thật
# khi pyfluidsynth thiếu (RENDER_ENGINE=bridge mặc định).
try:
from app.core import native_render
out_path, _dur = native_render.render_soundfont_offline(
sf_path=sf_path,
notes=[{
"pitch": ev["note"],
"velocity": float(ev["velocity"]) / 127.0,
"start_beat": ev["start_beat"],
"duration_beats": ev["duration_beats"],
} for ev in midi_events],
bpm=bpm,
sample_rate=self.sample_rate,
bank=soundfont_bank,
program=soundfont_program,
)
synth_buffer, _ = sf.read(out_path, dtype="float32")
if synth_buffer.ndim == 1:
synth_buffer = np.vstack([synth_buffer, synth_buffer])
else:
synth_buffer = synth_buffer.T
try:
os.remove(out_path)
except Exception:
pass
except Exception as e:
logger.warning("[RenderEngine] native soundfont render failed: %s", e)
synth_buffer = render_midi_events_to_audio(
midi_events=midi_events, sr=self.sample_rate, bpm=bpm, instrument='synth'
)
else:
if not HAS_PYFLUIDSYNTH:
logger.warning("[RenderEngine] pyfluidsynth not available, falling back to oscillator synth")
synth_buffer = render_midi_events_to_audio(
midi_events=midi_events, sr=self.sample_rate, bpm=bpm, instrument='synth'
)