fix(latency): threadpool renders + RENDER_LOCK + client pre-render cache
This commit is contained in:
+26
-24
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user