fix(latency): threadpool renders + RENDER_LOCK + client pre-render cache

This commit is contained in:
2026-08-16 20:21:48 +07:00
parent 90b5a63719
commit ed26e1706d
6 changed files with 139 additions and 42 deletions
+26 -24
View File
@@ -11,6 +11,7 @@ from app.core.vst_engine import (
HAS_PEDALBOARD,
HAS_PYFLUIDSYNTH,
apply_preset_to_plugin,
RENDER_LOCK,
)
logger = logging.getLogger(__name__)
@@ -451,30 +452,31 @@ class PythonRenderEngine:
return session_buffer
def render_project(self, project_json: dict, output_filepath: str):
bpm = project_json["metadata"]["bpm"]
time_sig_num = project_json["metadata"].get("time_signature_numerator", 4)
main_session = project_json["main_session"]
section_store = project_json.get("section_store", {})
with RENDER_LOCK:
bpm = project_json["metadata"]["bpm"]
time_sig_num = project_json["metadata"].get("time_signature_numerator", 4)
main_session = project_json["main_session"]
section_store = project_json.get("section_store", {})
# Compute total project samples
total_bars = main_session.get("length_bars", 16.0)
total_samples = self.bars_to_samples(total_bars, bpm, time_sig_num)
# Render main session
master_buffer = self.render_session_container(
session=main_session,
section_store=section_store,
bpm=bpm,
time_sig_num=time_sig_num,
total_samples=total_samples,
_cache={},
)
# Compute total project samples
total_bars = main_session.get("length_bars", 16.0)
total_samples = self.bars_to_samples(total_bars, bpm, time_sig_num)
# Normalization to prevent clipping
max_peak = np.max(np.abs(master_buffer))
if max_peak > 1.0:
master_buffer /= max_peak
# Render main session
master_buffer = self.render_session_container(
session=main_session,
section_store=section_store,
bpm=bpm,
time_sig_num=time_sig_num,
total_samples=total_samples,
_cache={},
)
# Write final output file
sf.write(output_filepath, master_buffer.T, self.sample_rate)
return output_filepath
# Normalization to prevent clipping
max_peak = np.max(np.abs(master_buffer))
if max_peak > 1.0:
master_buffer /= max_peak
# Write final output file
sf.write(output_filepath, master_buffer.T, self.sample_rate)
return output_filepath
+16
View File
@@ -3,8 +3,21 @@ import os
import json
import numpy as np
import functools
import threading
from ctypes import c_char_p
# pedalboard VST3 + pyfluidsynth KHÔNG thread-safe: tuần tự hóa mọi native render
RENDER_LOCK = threading.RLock()
def _render_locked(fn):
"""Chạy hàm native (VST3/pyfluidsynth) dưới RENDER_LOCK — không thread-safe."""
@functools.wraps(fn)
def wrapper(*args, **kwargs):
with RENDER_LOCK:
return fn(*args, **kwargs)
return wrapper
def _ensure_fluidsynth_dll_on_path():
"""Frozen (PyInstaller): pyfluidsynth tim dll qua ctypes.util.find_library
@@ -25,6 +38,7 @@ def midi_note_to_freq(note_number: int) -> float:
return 440.0 * (2.0 ** ((note_number - 69) / 12.0))
@_render_locked
def render_soundfont_midi_to_audio(midi_events: list, sf_path: str, bank: int = 0,
program: int = 0, sr: int = 44100, bpm: float = 120.0) -> np.ndarray:
"""Native FluidSynth (pyfluidsynth) — render MIDI events bằng soundfont THẬT.
@@ -92,6 +106,7 @@ def render_soundfont_midi_to_audio(midi_events: list, sf_path: str, bank: int =
except Exception:
pass
@_render_locked
def render_midi_events_to_audio(midi_events: list, sr: int = 44100, bpm: float = 120.0, instrument: str = 'synth') -> np.ndarray:
beat_duration_sec = 60.0 / max(30.0, bpm)
max_duration_sec = 2.0
@@ -336,6 +351,7 @@ class PluginManager:
}
return list(sf_map.values())
@_render_locked
def load_vst(self, plugin_name: str, preset_data: dict = None):
if not HAS_PEDALBOARD:
return None