V45: D17 seamless loop wrap + D18 stop solo note-off (Qin) + D18b/c guard + engine VST load fixes

- D17: wrap_info/wrap-skip, fx wrap xfade, playhead-derived scheduling
- D18: solo stop -> NOTE_OFF all sounding timeline pitches + CC123/CC120
- D18b: track audible gain guard, tracksRef fallback
- D18c: timelinePitchesRef per-track Set, kill at applyAllTrackMuteSolo
- bridge ring _MAX 16, index v=202608290928
- plugins.py/vst_engine.py/fx_realtime.py/JuceFxEngine.cpp: VSTi load + Qin fixes (uncommitted 12:18)
This commit is contained in:
2026-08-29 16:39:02 +07:00
parent 2afa828c8f
commit c084ac8ba7
9 changed files with 434 additions and 104 deletions
+4 -3
View File
@@ -23,10 +23,7 @@ import threading
import time
import uuid
import numpy as np
from app.config import settings
from app.core.native_render import find_fx_bridge_exe, find_juce_fx_bridge_exe
FXRT_MAGIC = 0x46585254
FXRT_BLOCK = 256
@@ -91,6 +88,7 @@ class FxRealtimeSession:
"""Một session realtime: SHM + subprocess fx_vst_bridge + I/O block."""
def __init__(self, fx_chain, sample_rate, block_size=FXRT_BLOCK):
import numpy as np
from multiprocessing import shared_memory
self.fx_chain = fx_chain or []
self.sample_rate = int(sample_rate)
@@ -163,6 +161,7 @@ class FxRealtimeSession:
return out
def _spawn(self):
from app.core.native_render import find_fx_bridge_exe, find_juce_fx_bridge_exe
juce_exe = os.getenv("SF_JUCE_FX_BRIDGE_PATH", "").strip()
# G3: mặc định dùng JUCE bridge (latency graph thật + processed samples);
# fallback bridge cũ khi không có exe JUCE (regression an toàn).
@@ -265,6 +264,7 @@ class FxRealtimeSession:
"""Gom FXRT_IN_SLOTS frame vào pending, đủ 4 → publish atomic vào ring
(ghi data toàn bộ slot trước, sau đó in_write += n 1 lần). Ring đầy khi
publish → drop batch cũ nhất (giữ latency thấp)."""
import numpy as np
arr = np.frombuffer(interleaved, dtype=np.float32)
if arr.size != self.block_size * 2:
return
@@ -333,6 +333,7 @@ class FxRealtimeSession:
self.stats['flush'] += 1
def read_output(self):
import numpy as np
"""Drain output ring → list bytes (mỗi block 2048 bytes interleaved)."""
out = []
with self._lock: