Fix audio timeout when changing buffer size/sample rate
Two bugs: 1. **Deadlock in set_audio_profile() (root cause of timeout)** jack_client.stop() was called BEFORE killing the JACK server. jack.Client.deactivate() is a synchronous call that blocks until the JACK RT callback thread acknowledges. When ALSA reconfigures (period/rate change), the RT thread can stall and deactivate() hangs permanently. Reversed order: kill JACK server first (via killall jackd) to force-kill the RT thread, then clean up the Python client. Same fix applied to device hot-swap path. 2. **Pipeline crash after period change** _CombFilter.buf and _AllpassFilter.buf were fixed-size arrays at BLOCK_SIZE (256). If JACK restarts with a different period (e.g. 512), self.buf[:] = block throws a shape mismatch in the RT callback -> audio dies permanently. Made internal buffers dynamically resize to match input block size.
This commit is contained in:
+14
-2
@@ -228,7 +228,7 @@ class _DelayLine:
|
||||
class _CombFilter:
|
||||
"""Comb filter for Schroeder reverb."""
|
||||
|
||||
__slots__ = ("delay", "feedback", "damping", "damp_filt", "buf")
|
||||
__slots__ = ("delay", "feedback", "damping", "damp_filt", "buf", "_buf_size")
|
||||
|
||||
def __init__(self, delay_samples: int):
|
||||
line_len = max(BLOCK_SIZE * 2, delay_samples + 1)
|
||||
@@ -237,8 +237,14 @@ class _CombFilter:
|
||||
self.damping: float = 0.5 # low-pass damping coefficient
|
||||
self.damp_filt: float = 0.0 # state variable for damping
|
||||
self.buf = np.zeros(BLOCK_SIZE, dtype=np.float32)
|
||||
self._buf_size = BLOCK_SIZE
|
||||
|
||||
def process(self, block: np.ndarray) -> np.ndarray:
|
||||
# Resize internal buffer if block size changed (e.g. JACK period switch)
|
||||
n = len(block)
|
||||
if n != self._buf_size:
|
||||
self.buf = np.zeros(n, dtype=np.float32)
|
||||
self._buf_size = n
|
||||
self.buf[:] = block
|
||||
# Write with feedback: out[n] = in[n] + feedback * damped_delayed
|
||||
delayed = self.delay.add_to_block(self.buf, self.delay.max_len - 1, self.feedback)
|
||||
@@ -255,15 +261,21 @@ class _CombFilter:
|
||||
class _AllpassFilter:
|
||||
"""Allpass filter for Schroeder reverb."""
|
||||
|
||||
__slots__ = ("delay", "gain", "buf")
|
||||
__slots__ = ("delay", "gain", "buf", "_buf_size")
|
||||
|
||||
def __init__(self, delay_samples: int):
|
||||
line_len = max(BLOCK_SIZE * 2, delay_samples + 1)
|
||||
self.delay = _DelayLine(line_len)
|
||||
self.gain: float = 0.5
|
||||
self.buf = np.zeros(BLOCK_SIZE, dtype=np.float32)
|
||||
self._buf_size = BLOCK_SIZE
|
||||
|
||||
def process(self, block: np.ndarray) -> np.ndarray:
|
||||
# Resize internal buffer if block size changed (e.g. JACK period switch)
|
||||
n = len(block)
|
||||
if n != self._buf_size:
|
||||
self.buf = np.zeros(n, dtype=np.float32)
|
||||
self._buf_size = n
|
||||
# out[n] = -gain * in[n] + delay[n - D] + gain * delay_output[n - D]
|
||||
# Standard allpass: out = -g * in + delayed + g * delayed_out
|
||||
# But block-wise: read delayed, write in + g * delayed, output = -g * in + delayed
|
||||
|
||||
Reference in New Issue
Block a user