fix: move NAM block size sync before JACK client restart
The profile change handler was calling nam_host.set_block_size() AFTER jack_client.start(), creating a race where the JACK callback fired with the new buffer size while the old NAM subprocess was still running at the old block_size. Pipe byte counts desynced => NAM returned passthrough. + also: auto_connect disable during restart, connect_fx_ports reorder, Tone3000 gear/sort/pagination params, NAM engine switch API endpoints
This commit is contained in:
+50
-9
@@ -1051,6 +1051,29 @@ class WebServer:
|
||||
"current": nam.current_model.name if nam.current_model else None,
|
||||
}
|
||||
|
||||
@app.get("/api/nam/engine")
|
||||
async def get_nam_engine(channel: str = "guitar"):
|
||||
"""Get current NAM engine mode."""
|
||||
_pm, _pl, nam, _ir = self._channel_deps(channel)
|
||||
if not nam:
|
||||
raise HTTPException(status_code=503)
|
||||
mode = getattr(nam, 'engine_mode', 'cpp')
|
||||
return {"engine_mode": mode}
|
||||
|
||||
@app.post("/api/nam/engine")
|
||||
async def set_nam_engine(data: dict, channel: str = "guitar"):
|
||||
"""Switch NAM engine between 'cpp' and 'pytorch'."""
|
||||
_pm, _pl, nam, _ir = self._channel_deps(channel)
|
||||
if not nam:
|
||||
raise HTTPException(status_code=503)
|
||||
mode = data.get("engine_mode", "cpp")
|
||||
if mode not in ("cpp", "pytorch"):
|
||||
raise HTTPException(status_code=400, detail="engine_mode must be 'cpp' or 'pytorch'")
|
||||
ok = nam.set_engine(mode)
|
||||
if not ok:
|
||||
raise HTTPException(status_code=500, detail=f"Failed to switch to {mode} engine")
|
||||
return {"ok": True, "engine_mode": mode}
|
||||
|
||||
@app.post("/api/models/load")
|
||||
async def load_model(data: dict, channel: str = "guitar"):
|
||||
"""Load a NAM model by path for the given channel."""
|
||||
@@ -1486,12 +1509,17 @@ class WebServer:
|
||||
logger.warning("jack_client.stop() failed (expected if JACK was mid-callback)")
|
||||
|
||||
# Start JACK with new parameters
|
||||
ok = audio_sys.start_jack(timeout=10)
|
||||
# Disable auto_connect temporarily — fx_in/fx_out ports don't
|
||||
# exist until jack_client.start() recreates the JACK client below
|
||||
saved_auto_connect = audio_sys.config.auto_connect
|
||||
audio_sys.config.auto_connect = False
|
||||
ok = audio_sys.start_jack(timeout=15)
|
||||
audio_sys.config.auto_connect = saved_auto_connect
|
||||
if not ok:
|
||||
# Rollback on failure
|
||||
audio_sys.config.profile = old_key
|
||||
logger.warning("JACK restart failed — attempting rollback to %s", old_key)
|
||||
audio_sys.start_jack(timeout=10)
|
||||
audio_sys.start_jack(timeout=15)
|
||||
if jack_client:
|
||||
try:
|
||||
jack_client.start()
|
||||
@@ -1502,6 +1530,13 @@ class WebServer:
|
||||
detail=f"Failed to start JACK with period={target_profile['period']}, rate={target_profile['rate']} — rolled back",
|
||||
)
|
||||
|
||||
# Sync NAM engine block size BEFORE restarting JACK client
|
||||
# so the NAM subprocess is already sized for the new period
|
||||
# when the process callback starts firing.
|
||||
nam_host = self.deps.nam_host
|
||||
if nam_host and hasattr(nam_host, 'set_block_size'):
|
||||
nam_host.set_block_size(target_profile["period"])
|
||||
|
||||
# Restart JACK audio client
|
||||
if jack_client:
|
||||
try:
|
||||
@@ -1509,10 +1544,12 @@ class WebServer:
|
||||
except Exception as e:
|
||||
logger.warning("jack_client.start() failed after profile switch: %s", e)
|
||||
|
||||
# Sync NAM engine block size
|
||||
nam_host = self.deps.nam_host
|
||||
if nam_host and hasattr(nam_host, 'set_block_size'):
|
||||
nam_host.set_block_size(target_profile["period"])
|
||||
# Reconnect FX ports now that pipeline's JACK client is alive
|
||||
if saved_auto_connect:
|
||||
try:
|
||||
audio_sys.connect_fx_ports()
|
||||
except Exception as e:
|
||||
logger.warning("connect_fx_ports() failed after profile switch: %s", e)
|
||||
|
||||
# ── Persist to config.yaml ──────────────────────────────
|
||||
if self.deps.config is not None:
|
||||
@@ -1751,7 +1788,7 @@ class WebServer:
|
||||
return {"online": ok}
|
||||
|
||||
@app.get("/api/models/tonedownload/search")
|
||||
async def tonedownload_search_models(q: str = "", page: int = 0, arch: str = ""):
|
||||
async def tonedownload_search_models(q: str = "", page: int = 0, arch: str = "", gear: str = "", sort: str = "created_at.desc"):
|
||||
"""Search Tone3000 for NAM models."""
|
||||
try:
|
||||
client = await self._get_tonedownload()
|
||||
@@ -1765,10 +1802,11 @@ class WebServer:
|
||||
},
|
||||
)
|
||||
arch_val = arch.strip() or None
|
||||
gear_val = gear.strip() or None
|
||||
if not q.strip():
|
||||
results = await client.get_trending_models(arch=arch_val)
|
||||
results = await client.get_trending_models(arch=arch_val, gear=gear_val)
|
||||
else:
|
||||
results = await client.search_models(q.strip(), page=page, arch=arch_val)
|
||||
results = await client.search_models(q.strip(), page=page, arch=arch_val, gear=gear_val, sort=sort)
|
||||
return {
|
||||
"results": [
|
||||
{
|
||||
@@ -1786,9 +1824,12 @@ class WebServer:
|
||||
"pack_name": r.pack_name,
|
||||
"architecture": r.architecture,
|
||||
"created_at": r.created_at,
|
||||
"downloads": r.downloads,
|
||||
"likes": r.likes,
|
||||
}
|
||||
for r in results
|
||||
],
|
||||
"page": page,
|
||||
}
|
||||
except Exception as e:
|
||||
logger.warning("Tone3000 model search failed: %s", e)
|
||||
|
||||
Reference in New Issue
Block a user