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(), which means the JACK process callback started firing
with the new buffer size while the old NAM subprocess was still running
at the old block size. The pipe read/write byte counts desynchronized,
causing NAMEngineProcess.process() to return audio_block as passthrough
(direct input / DI signal) — every subsequent callback showed dry audio.

Fix: reload the NAM engine with the correct block size BEFORE restarting
the JACK client. Now when the process callback fires, the NAM subprocess
is already ready with the matching block size.
This commit is contained in:
2026-06-17 21:07:48 +00:00
parent 1b2747abb1
commit 5dc769ad30
8 changed files with 1648 additions and 66 deletions
+44 -5
View File
@@ -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()
@@ -1509,6 +1537,13 @@ class WebServer:
except Exception as e:
logger.warning("jack_client.start() failed after profile switch: %s", e)
# 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)
# Sync NAM engine block size
nam_host = self.deps.nam_host
if nam_host and hasattr(nam_host, 'set_block_size'):
@@ -1751,7 +1786,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 +1800,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 +1822,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)