fix: kill alsa_in in stop_jack; verify jackd matches config device
CI / test (push) Has been cancelled
CI / test (push) Has been cancelled
Two-part fix for the rogue jackd race: 1. stop_jack() now kills alsa_in (from bt-a2dp bridge). When JACK dies, alsa_in auto-spawns a rogue jackd -T -ndefault -d alsa that satisfies _jack_is_operational() but is the wrong server. 2. _jack_is_operational() now paired with _jack_matches_config() which verifies the running jackd's /proc/cmdline contains the expected ALSA device string (e.g. hw:USB,0). A rogue jackd without this won't pass the readiness check. Also added _jack_matches_config() helper that scans /proc/*/cmdline.
This commit is contained in:
+43
-3
@@ -294,8 +294,9 @@ class AudioSystem:
|
||||
# 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 JACK is actually accepting connections
|
||||
if _jack_is_operational():
|
||||
# Verify JACK is actually accepting connections AND matches
|
||||
# our device (ignore rogue jackd -T from bt-a2dp's alsa_in)
|
||||
if _jack_is_operational() and _jack_matches_config(self.config.output_device):
|
||||
logger.info("JACK already running and operational — reusing")
|
||||
return True
|
||||
# Process exists but can't connect — likely a zombie that
|
||||
@@ -356,7 +357,7 @@ class AudioSystem:
|
||||
# Stabilization check — JACK can start briefly then crash
|
||||
# if the ALSA device is flaky. Wait 1.5s with retries.
|
||||
time.sleep(0.5)
|
||||
if _jack_is_operational():
|
||||
if _jack_is_operational() and _jack_matches_config(self.config.output_device):
|
||||
# Auto-connect ports if enabled
|
||||
if self.config.auto_connect:
|
||||
self.connect_fx_ports()
|
||||
@@ -388,6 +389,16 @@ class AudioSystem:
|
||||
)
|
||||
except Exception:
|
||||
pass
|
||||
# Kill alsa_in — bt-a2dp's bridge uses it, and when JACK dies
|
||||
# alsa_in auto-spawns a rogue jackd -T -ndefault that satisfies
|
||||
# _jack_is_operational() but connects as the wrong server.
|
||||
try:
|
||||
subprocess.run(
|
||||
["killall", "alsa_in"],
|
||||
capture_output=True, timeout=3,
|
||||
)
|
||||
except Exception:
|
||||
pass
|
||||
try:
|
||||
subprocess.run(
|
||||
["killall", "jackd"],
|
||||
@@ -879,6 +890,35 @@ def _jack_is_operational() -> bool:
|
||||
return False
|
||||
|
||||
|
||||
def _jack_matches_config(cmdline_match: str = "") -> bool:
|
||||
"""Check if the running jackd command matches expected args.
|
||||
|
||||
On systems with bt-a2dp-jack, ``alsa_in`` can auto-spawn a rogue
|
||||
``jackd -T -ndefault`` that satisfies ``_jack_is_operational()``
|
||||
but uses the wrong server name/ALSA device. Only return True if
|
||||
the running jackd's ``/proc/*/cmdline`` contains the expected
|
||||
device string.
|
||||
|
||||
Args:
|
||||
cmdline_match: String to match in the jackd cmdline
|
||||
(e.g. ``hw:USB,0`` or a period value).
|
||||
"""
|
||||
if not cmdline_match:
|
||||
return True # no filter — just return operational status
|
||||
try:
|
||||
import glob as _glob
|
||||
for proc_dir in _glob.glob("/proc/[0-9]*"):
|
||||
try:
|
||||
cmd = (Path(proc_dir) / "cmdline").read_bytes().decode("utf-8", errors="replace").replace("\0", " ")
|
||||
if "jackd" in cmd and cmdline_match in cmd:
|
||||
return True
|
||||
except (OSError, PermissionError):
|
||||
continue
|
||||
return False
|
||||
except Exception:
|
||||
return True # on error, don't block — rely on operational check
|
||||
|
||||
|
||||
# ═══════════════════════════════════════════════════════════════════
|
||||
# JACK audio client (real-time I/O with pipeline)
|
||||
# ═══════════════════════════════════════════════════════════════════
|
||||
|
||||
Reference in New Issue
Block a user