From 9a8c59d19c78016a20dd5debabbe67bcd87f09a1 Mon Sep 17 00:00:00 2001 From: Shawn Date: Thu, 18 Jun 2026 17:59:30 -0400 Subject: [PATCH] fix: replace broken jack_default_0_0 socket check with jack_lsp for JACK 1.9.22 compatibility start_jack() was checking for /dev/shm/jack_default_0_0 which never exists on JACK 1.9.22 (used on RPi/DietPi). The check made start_jack() always timeout and return False. Boot sequence ignored the return value (luck), but the profile change handler checked it via 'if not ok: rollback', causing all buffer/rate changes to fail silently every other time. Replace with _jack_is_operational() using jack_lsp which tests actual JACK IPC and works correctly on all JACK versions. --- src/system/audio.py | 39 ++++++++++++++++++++++++++++++--------- 1 file changed, 30 insertions(+), 9 deletions(-) diff --git a/src/system/audio.py b/src/system/audio.py index 7288b44..e2f5fa7 100644 --- a/src/system/audio.py +++ b/src/system/audio.py @@ -291,13 +291,14 @@ class AudioSystem: # Already running? Check that the running JACK matches our # desired config — a zombie jackd from another process can # satisfy _jack_is_running() but have wrong settings. + # IMPORTANT: On JACK 1.9.22 there is no /dev/shm/jack_default_0_0 + # socket. Instead, verify via jack_lsp which tests actual IPC. if _jack_is_running(): - # Verify via /dev/shm/jack_default_0_0 socket that the - # running JACK is actually accepting connections. - if Path("/dev/shm/jack_default_0_0").is_socket(): + # Verify JACK is actually accepting connections + if _jack_is_operational(): logger.info("JACK already running and operational — reusing") return True - # Process exists but no socket — likely a zombie that + # Process exists but can't connect — likely a zombie that # spawned but failed to init audio. Kill it first. logger.warning("JACK process exists but not operational — killing") try: @@ -345,10 +346,9 @@ class AudioSystem: while time.monotonic() < deadline: time.sleep(0.3) if _jack_is_running(): - # Double-check JACK is actually operational by testing - # the SHM socket — jack_default_0_0 is created only - # after the ALSA device is successfully initialized. - if Path("/dev/shm/jack_default_0_0").is_socket(): + # Verify JACK is actually operational via jack_lsp + # (jack_default_0_0 socket doesn't exist on JACK 1.9.22) + if _jack_is_operational(): logger.info( "JACK started: period=%d, nperiods=%d, rate=%d", profile["period"], profile["nperiods"], profile["rate"], @@ -357,7 +357,7 @@ class AudioSystem: if self.config.auto_connect: self.connect_fx_ports() return True - # JACK process exists but SHM not ready yet — keep waiting + # JACK process exists but not ready yet — keep waiting continue # Timed out — check for common issues @@ -851,6 +851,27 @@ def _jack_is_running() -> bool: return False +def _jack_is_operational() -> bool: + """Check if JACK is actually accepting client connections. + + Uses ``jack_lsp`` to verify the JACK server is operational beyond + just having a running process. ``jack_lsp`` connects to the JACK + server via shared memory and lists ports — it only succeeds when + JACK has fully initialized the ALSA device and IPC is live. + + On JACK 1.9.22 there is no ``/dev/shm/jack_default_0_0`` socket, + so ``jack_lsp`` is the most reliable readiness check. + """ + try: + result = subprocess.run( + ["jack_lsp"], + capture_output=True, timeout=3, + ) + return result.returncode == 0 + except (FileNotFoundError, subprocess.TimeoutExpired): + return False + + # ═══════════════════════════════════════════════════════════════════ # JACK audio client (real-time I/O with pipeline) # ═══════════════════════════════════════════════════════════════════