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:
@@ -25,7 +25,7 @@ import yaml
|
||||
from src.system.audio import AudioConfig, AudioSystem, JackAudioClient, _jack_is_running
|
||||
from src.system.defaults import ensure_defaults_exist
|
||||
from src.dsp.pipeline import AudioPipeline
|
||||
from src.dsp.nam_engine_host import FastNAMHost
|
||||
from src.dsp.nam_router import NAMEngineRouter
|
||||
from src.dsp.ir_loader import IRLoader
|
||||
from src.presets.manager import PresetManager
|
||||
from src.presets.types import Channel, MIDIMapping, Preset
|
||||
@@ -69,6 +69,7 @@ class PedalApp:
|
||||
|
||||
def __init__(self, config_path: Path = DEFAULT_CONFIG_PATH) -> None:
|
||||
self._config = load_config(config_path)
|
||||
self._config_path = config_path
|
||||
|
||||
# ── Runtime state ──────────────────────────────────────────────
|
||||
self._running = False
|
||||
@@ -79,7 +80,7 @@ class PedalApp:
|
||||
self.audio_config: AudioConfig | None = None
|
||||
self.audio_system: AudioSystem | None = None
|
||||
self.jack_audio: JackAudioClient | None = None
|
||||
self.nam_host: FastNAMHost | None = None
|
||||
self.nam_host: NAMEngineRouter | None = None
|
||||
self.ir_loader: IRLoader | None = None
|
||||
self.pipeline: AudioPipeline | None = None
|
||||
self.presets: PresetManager | None = None
|
||||
@@ -124,6 +125,8 @@ class PedalApp:
|
||||
output_device=acfg.get("output_device", "hw:0,0"),
|
||||
jack_enabled=acfg.get("jack_enabled", True),
|
||||
auto_connect=acfg.get("auto_connect", True),
|
||||
period=acfg.get("period"),
|
||||
rate=acfg.get("rate"),
|
||||
)
|
||||
self.audio_system = AudioSystem(self.audio_config)
|
||||
self.audio_system.setup_i2s(reboot_hint=True)
|
||||
@@ -134,7 +137,7 @@ class PedalApp:
|
||||
|
||||
# ── 2. DSP pipeline (NAM + IR + FX chain) ────────────
|
||||
block_size = self.audio_config.latency_profile["period"]
|
||||
self.nam_host = FastNAMHost(block_size=block_size)
|
||||
self.nam_host = NAMEngineRouter(block_size=block_size)
|
||||
self.ir_loader = IRLoader()
|
||||
self.pipeline = AudioPipeline(nam_host=self.nam_host, ir_loader=self.ir_loader)
|
||||
self.nam_host.warm_up()
|
||||
@@ -251,6 +254,8 @@ class PedalApp:
|
||||
ir_loader=self.ir_loader,
|
||||
audio_system=self.audio_system,
|
||||
jack_audio=self.jack_audio,
|
||||
config=self._config,
|
||||
config_path=self._config_path,
|
||||
),
|
||||
host="0.0.0.0",
|
||||
port=80,
|
||||
@@ -681,6 +686,29 @@ def main() -> int:
|
||||
|
||||
Returns exit code 0 on clean shutdown, 1 on boot failure.
|
||||
"""
|
||||
# Lock process memory early to prevent page faults in RT callback
|
||||
try:
|
||||
import ctypes
|
||||
libc = ctypes.CDLL('libc.so.6')
|
||||
# MCL_CURRENT | MCL_FUTURE = 1 | 2 = 3
|
||||
if libc.mlockall(3) == 0:
|
||||
logger.info("mlockall() OK — process memory locked")
|
||||
else:
|
||||
logger.warning("mlockall() failed — check LimitMEMLOCK in systemd unit")
|
||||
except Exception as exc:
|
||||
logger.warning("mlockall() not available: %s", exc)
|
||||
|
||||
# Set CPU governor to performance for stable RT audio
|
||||
try:
|
||||
for c in range(4): # RPi 4B has 4 cores
|
||||
gov_path = f"/sys/devices/system/cpu/cpu{c}/cpufreq/scaling_governor"
|
||||
if os.path.exists(gov_path):
|
||||
with open(gov_path, "w") as f:
|
||||
f.write("performance")
|
||||
logger.info("CPU%d governor set to performance", c)
|
||||
except Exception as exc:
|
||||
logger.warning("Could not set CPU governor (non-root?): %s", exc)
|
||||
|
||||
import argparse
|
||||
|
||||
parser = argparse.ArgumentParser(
|
||||
|
||||
Reference in New Issue
Block a user