fix: add missing API endpoints for volume POST, bypass/toggle, block params, block toggle; fix JACK 1.9.22 cmd args; fix Scarlett 2i2 channel count
This commit is contained in:
+70
-12
@@ -12,6 +12,7 @@ Manages ALSA / JACK / I2S setup on RPi 4B:
|
||||
from __future__ import annotations
|
||||
|
||||
import logging
|
||||
import os
|
||||
import re
|
||||
import subprocess
|
||||
import time
|
||||
@@ -276,22 +277,28 @@ class AudioSystem:
|
||||
return True
|
||||
|
||||
profile = self.config.latency_profile
|
||||
jack_env = os.environ.copy()
|
||||
jack_env["JACK_NO_AUDIO_RESERVATION"] = "1"
|
||||
# Scarlett 2i2 needs 2 channels even in mono mode
|
||||
n_in = max(2, self.config.capture_channels)
|
||||
n_out = max(2, self.config.playback_channels)
|
||||
cmd = [
|
||||
"jackd",
|
||||
f"-P{profile['rt_priority']}",
|
||||
f"-p{profile['period']}",
|
||||
f"-n{profile['nperiods']}",
|
||||
f"-r{profile['rate']}",
|
||||
"-dalsa",
|
||||
f"-d{self.config.output_device}",
|
||||
f"-i{self.config.capture_channels}",
|
||||
f"-o{self.config.playback_channels}",
|
||||
"-P", str(profile['rt_priority']),
|
||||
"-d", "alsa",
|
||||
"-d", self.config.output_device,
|
||||
"-r", str(profile['rate']),
|
||||
"-p", str(profile['period']),
|
||||
"-n", str(profile['nperiods']),
|
||||
"-i", str(n_in),
|
||||
"-o", str(n_out),
|
||||
]
|
||||
logger.info("Starting JACK: %s", " ".join(cmd))
|
||||
|
||||
try:
|
||||
proc = subprocess.Popen(
|
||||
cmd,
|
||||
env=jack_env,
|
||||
stdout=subprocess.DEVNULL,
|
||||
stderr=subprocess.DEVNULL,
|
||||
)
|
||||
@@ -759,14 +766,24 @@ WantedBy=multi-user.target
|
||||
|
||||
|
||||
def _jack_is_running() -> bool:
|
||||
"""Check if JACK is running via jack_wait."""
|
||||
"""Check if JACK is running via process list or PID file."""
|
||||
try:
|
||||
result = subprocess.run(
|
||||
["jack_wait", "-c"],
|
||||
capture_output=True, text=True, timeout=5,
|
||||
["pgrep", "-x", "jackd"],
|
||||
capture_output=True, timeout=3,
|
||||
)
|
||||
return result.returncode == 0
|
||||
except (FileNotFoundError, subprocess.TimeoutExpired):
|
||||
except FileNotFoundError:
|
||||
# pgrep not available — fall back to pidof
|
||||
try:
|
||||
result = subprocess.run(
|
||||
["pidof", "jackd"],
|
||||
capture_output=True, timeout=3,
|
||||
)
|
||||
return result.returncode == 0
|
||||
except (FileNotFoundError, subprocess.TimeoutExpired):
|
||||
return False
|
||||
except subprocess.TimeoutExpired:
|
||||
return False
|
||||
|
||||
|
||||
@@ -869,6 +886,9 @@ class JackAudioClient:
|
||||
self._active = True
|
||||
logger.info("JACK client activated (blocksize=%d)", block_size)
|
||||
|
||||
# Explicitly connect ports to system (JACK auto-connect is unreliable)
|
||||
self._connect_ports()
|
||||
|
||||
def stop(self) -> None:
|
||||
"""Deactivate and close the JACK client."""
|
||||
if not self._active or self._client is None:
|
||||
@@ -889,6 +909,44 @@ class JackAudioClient:
|
||||
self._out_buf = None
|
||||
logger.info("JACK client stopped")
|
||||
|
||||
# ── Port connection helpers ──────────────────────────────────
|
||||
|
||||
def _connect_ports(self) -> None:
|
||||
"""Explicitly connect our ports to the system physical ports.
|
||||
|
||||
JACK's auto-connect is unreliable across systemd service restarts.
|
||||
This ensures audio always flows even when the JACK server was
|
||||
started before our client.
|
||||
"""
|
||||
if self._client is None:
|
||||
return
|
||||
|
||||
# Connect each capture port to the corresponding system capture
|
||||
for i, port in enumerate(self._inports):
|
||||
suffix = f"capture_{i + 1}" if self._input_channels > 1 else "capture_1"
|
||||
sys_port_name = f"system:{suffix}"
|
||||
try:
|
||||
sys_ports = self._client.get_ports(sys_port_name)
|
||||
if sys_ports:
|
||||
self._client.connect(sys_ports[0], port)
|
||||
logger.debug("Connected %s -> %s", sys_port_name, port.name)
|
||||
except Exception as exc:
|
||||
logger.warning("Failed to connect %s -> %s: %s",
|
||||
sys_port_name, port.name, exc)
|
||||
|
||||
# Connect each playback port to the corresponding system playback
|
||||
for i, port in enumerate(self._outports):
|
||||
suffix = f"playback_{i + 1}" if self._output_channels > 1 else "playback_1"
|
||||
sys_port_name = f"system:{suffix}"
|
||||
try:
|
||||
sys_ports = self._client.get_ports(sys_port_name)
|
||||
if sys_ports:
|
||||
self._client.connect(port, sys_ports[0])
|
||||
logger.debug("Connected %s -> %s", port.name, sys_port_name)
|
||||
except Exception as exc:
|
||||
logger.warning("Failed to connect %s -> %s: %s",
|
||||
port.name, sys_port_name, exc)
|
||||
|
||||
# ── Runtime reconfiguration ────────────────────────────────────
|
||||
|
||||
def set_channel_count(self, input_channels: int, output_channels: int) -> None:
|
||||
|
||||
Reference in New Issue
Block a user